@xnoxs/flux-lang 3.2.0 → 3.2.2

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.
@@ -1,399 +0,0 @@
1
- 'use strict';
2
-
3
- // ── CSS property shorthands ───────────────────────────────────────────────────
4
- const CSS_PROP_MAP = {
5
- bg: 'background', fg: 'color', p: 'padding',
6
- px: 'padding-inline', py: 'padding-block',
7
- pt: 'padding-top', pb: 'padding-bottom', pl: 'padding-left', pr: 'padding-right',
8
- m: 'margin', mx: 'margin-inline', my: 'margin-block',
9
- mt: 'margin-top', mb: 'margin-bottom', ml: 'margin-left', mr: 'margin-right',
10
- radius: 'border-radius',
11
- w: 'width', h: 'height',
12
- 'min-w': 'min-width', 'max-w': 'max-width', 'min-h': 'min-height', 'max-h': 'max-height',
13
- gap: 'gap', 'col-gap': 'column-gap', 'row-gap': 'row-gap',
14
- text: 'font-size', font: 'font-family', weight: 'font-weight',
15
- tracking: 'letter-spacing', leading: 'line-height',
16
- shadow: 'box-shadow', opacity: 'opacity',
17
- border: 'border', outline: 'outline',
18
- transition: 'transition', cursor: 'cursor', overflow: 'overflow',
19
- 'overflow-x': 'overflow-x', 'overflow-y': 'overflow-y',
20
- z: 'z-index', transform: 'transform', content: 'content',
21
- resize: 'resize', appearance: 'appearance',
22
- 'accent-color': 'accent-color', 'object-fit': 'object-fit',
23
- direction: 'flex-direction', wrap: 'flex-wrap',
24
- align: 'align-items', justify: 'justify-content',
25
- 'align-self': 'align-self', 'justify-self': 'justify-self',
26
- grow: 'flex-grow', shrink: 'flex-shrink', basis: 'flex-basis',
27
- order: 'order', cols: 'grid-template-columns', rows: 'grid-template-rows',
28
- 'col-span': 'grid-column', 'row-span': 'grid-row',
29
- 'place-items': 'place-items', 'place-content': 'place-content',
30
- 'list-style': 'list-style', 'text-align': 'text-align',
31
- decoration: 'text-decoration', 'text-transform': 'text-transform',
32
- 'white-space': 'white-space', 'word-break': 'word-break',
33
- 'user-select': 'user-select', 'pointer-events': 'pointer-events',
34
- 'vertical-align': 'vertical-align', 'backdrop': 'backdrop-filter',
35
- filter: 'filter', clip: 'clip-path', animation: 'animation',
36
- position: 'position', top: 'top', right: 'right', bottom: 'bottom',
37
- left: 'left', inset: 'inset', color: 'color', background: 'background',
38
- };
39
-
40
- // ── Boolean shorthands (no value needed) ─────────────────────────────────────
41
- const CSS_BOOL_MAP = {
42
- flex: 'display: flex', grid: 'display: grid',
43
- block: 'display: block', inline: 'display: inline',
44
- 'inline-flex': 'display: inline-flex', 'inline-block': 'display: inline-block',
45
- 'inline-grid': 'display: inline-grid',
46
- bold: 'font-weight: 700', italic: 'font-style: italic',
47
- relative: 'position: relative', absolute: 'position: absolute',
48
- fixed: 'position: fixed', sticky: 'position: sticky',
49
- hidden: 'display: none', pointer: 'cursor: pointer',
50
- underline: 'text-decoration: underline',
51
- 'line-through': 'text-decoration: line-through',
52
- capitalize: 'text-transform: capitalize',
53
- uppercase: 'text-transform: uppercase',
54
- lowercase: 'text-transform: lowercase',
55
- truncate: 'overflow: hidden; text-overflow: ellipsis; white-space: nowrap',
56
- 'select-none': 'user-select: none',
57
- 'no-wrap': 'white-space: nowrap',
58
- 'no-list': 'list-style: none',
59
- 'no-outline': 'outline: none',
60
- 'no-border': 'border: none',
61
- 'no-bg': 'background: transparent',
62
- 'no-padding': 'padding: 0',
63
- 'no-margin': 'margin: 0',
64
- 'no-resize': 'resize: none',
65
- center: 'text-align: center',
66
- 'items-center': 'align-items: center',
67
- 'justify-center': 'justify-content: center',
68
- 'place-center': 'place-items: center',
69
- 'flex-col': 'flex-direction: column',
70
- 'flex-row': 'flex-direction: row',
71
- 'flex-wrap': 'flex-wrap: wrap',
72
- 'flex-1': 'flex: 1',
73
- 'w-full': 'width: 100%',
74
- 'h-full': 'height: 100%',
75
- 'w-screen': 'width: 100vw',
76
- 'h-screen': 'height: 100vh',
77
- 'box-border': 'box-sizing: border-box',
78
- };
79
-
80
- function expandProp(name) {
81
- const t = name.trim();
82
- return CSS_PROP_MAP[t] || t;
83
- }
84
-
85
- function stripQuotes(val) {
86
- const v = val.trim();
87
- if (v.length >= 2 &&
88
- ((v[0] === '"' && v[v.length - 1] === '"') ||
89
- (v[0] === "'" && v[v.length - 1] === "'"))) {
90
- return v.slice(1, -1);
91
- }
92
- return v;
93
- }
94
-
95
- // Resolve a child selector against its parent
96
- function resolveSelector(sel, parent) {
97
- sel = sel.trim();
98
- if (!parent) return sel;
99
- // At-rules and @keyframe frames inside @keyframes don't get prefixed
100
- if (sel.startsWith('@')) return sel;
101
- if (parent.startsWith('@keyframes') || parent.startsWith('@font-face')) return sel;
102
- if (sel.includes('&')) return sel.replace(/&/g, parent);
103
- return parent + ' ' + sel;
104
- }
105
-
106
- // Expand a single declaration string (possibly multiple semicolon-separated)
107
- function expandDecls(text) {
108
- const result = [];
109
- text.split(';').forEach(part => {
110
- const d = part.trim();
111
- if (!d || d.startsWith('//')) return;
112
- if (CSS_BOOL_MAP[d]) {
113
- CSS_BOOL_MAP[d].split(';').forEach(bd => { const t = bd.trim(); if (t) result.push(t); });
114
- return;
115
- }
116
- const ci = d.indexOf(':');
117
- if (ci !== -1) {
118
- const prop = expandProp(d.slice(0, ci));
119
- const val = stripQuotes(d.slice(ci + 1));
120
- if (val !== '') result.push(prop + ': ' + val);
121
- }
122
- });
123
- return result;
124
- }
125
-
126
- // ── Parse CSS block content into a flat CSS string ────────────────────────────
127
- // Supports: nested selectors, &:pseudo, @keyframes, @media, boolean shorthands
128
- function parseCssBlockContent(content, indentLevel) {
129
- const ind = ' '.repeat(indentLevel || 0);
130
- const ind1 = ind + ' ';
131
- const lines = content.split('\n');
132
-
133
- // We output in two passes:
134
- // 1. Regular flattened rules (selector → [decls]) in a Map (preserves order)
135
- // 2. At-rule blocks (@keyframes, @media, etc.) collected separately
136
-
137
- const flatRules = new Map(); // selector → string[]
138
- const atRuleSegs = []; // raw at-rule CSS strings (output after flat rules)
139
- const selStack = [];
140
-
141
- function curSel() { return selStack.length ? selStack[selStack.length - 1] : null; }
142
-
143
- function addDecl(sel, d) {
144
- if (!flatRules.has(sel)) flatRules.set(sel, []);
145
- flatRules.get(sel).push(d);
146
- }
147
-
148
- function addDeclarationLine(line, sel) {
149
- // Support multiple declarations on one line: "p: 10px; m: 0; bold"
150
- if (line.includes(';')) {
151
- line.split(';').forEach(part => {
152
- const t = part.trim();
153
- if (t) addDeclarationLine(t, sel);
154
- });
155
- return;
156
- }
157
- // Boolean shorthand?
158
- if (CSS_BOOL_MAP[line]) {
159
- CSS_BOOL_MAP[line].split(';').forEach(bd => { const t = bd.trim(); if (t) addDecl(sel, t); });
160
- return;
161
- }
162
- const ci = line.indexOf(':');
163
- if (ci !== -1) {
164
- const prop = expandProp(line.slice(0, ci));
165
- const val = stripQuotes(line.slice(ci + 1));
166
- if (val !== '') addDecl(sel, prop + ': ' + val);
167
- }
168
- }
169
-
170
- // ── Collect a balanced brace block from `lines` starting at index i ──────
171
- // Returns { inner: string, nextI: number }
172
- function collectBlock(startI) {
173
- let depth = 1;
174
- let inner = '';
175
- let j = startI;
176
- while (j < lines.length && depth > 0) {
177
- const ln = lines[j++];
178
- for (const ch of ln) {
179
- if (ch === '{') depth++;
180
- else if (ch === '}') depth--;
181
- }
182
- if (depth > 0) inner += ln + '\n';
183
- else {
184
- // Last line: take content before final }
185
- const ci = ln.lastIndexOf('}');
186
- if (ci > 0) inner += ln.slice(0, ci) + '\n';
187
- }
188
- }
189
- return { inner, nextI: j };
190
- }
191
-
192
- let i = 0;
193
- while (i < lines.length) {
194
- const raw = lines[i++];
195
- const line = raw.trim();
196
- if (!line || line.startsWith('//')) continue;
197
-
198
- const ob = line.indexOf('{');
199
- const cb = line.lastIndexOf('}');
200
-
201
- // ── @keyframes / @font-face — emit as nested block (not flattened) ──────
202
- if (line.startsWith('@keyframes') || line.startsWith('@font-face')) {
203
- const atSel = ob !== -1 ? line.slice(0, ob).trim() : line.trim();
204
- let atBody = '';
205
-
206
- if (ob !== -1 && cb !== -1 && cb > ob) {
207
- // Inline: @keyframes name { from { ... } to { ... } }
208
- atBody = line.slice(ob + 1, cb);
209
- } else if (ob !== -1) {
210
- const res = collectBlock(i);
211
- i = res.nextI;
212
- atBody = res.inner;
213
- }
214
-
215
- // Expand property shorthands inside keyframe body (simple line pass)
216
- const expandedBody = atBody.split('\n').map(ln => {
217
- const t = ln.trim();
218
- if (!t || t === '{' || t === '}') return ln;
219
- const colonIdx = t.indexOf(':');
220
- if (colonIdx !== -1 && !t.slice(0, colonIdx).includes('{')) {
221
- const prop = expandProp(t.slice(0, colonIdx));
222
- const val = stripQuotes(t.slice(colonIdx + 1));
223
- return ln.replace(t, prop + ': ' + val);
224
- }
225
- return ln;
226
- }).join('\n');
227
-
228
- atRuleSegs.push(`${ind}${atSel} {\n${expandedBody}${ind}}\n`);
229
- continue;
230
- }
231
-
232
- // ── @media / @supports — emit as nested block, process inner rules ───────
233
- if (line.startsWith('@media') || line.startsWith('@supports') || line.startsWith('@layer')) {
234
- const atSel = ob !== -1 ? line.slice(0, ob).trim() : line.trim();
235
- let atBody = '';
236
-
237
- if (ob !== -1 && cb !== -1 && cb > ob) {
238
- atBody = line.slice(ob + 1, cb);
239
- } else if (ob !== -1) {
240
- const res = collectBlock(i);
241
- i = res.nextI;
242
- atBody = res.inner;
243
- }
244
-
245
- const innerCss = parseCssBlockContent(atBody, (indentLevel || 0) + 1);
246
- atRuleSegs.push(`${ind}${atSel} {\n${innerCss}${ind}}\n`);
247
- continue;
248
- }
249
-
250
- // ── Inline rule: selector { decl; decl } on a single line ──────────────
251
- if (ob !== -1 && cb !== -1 && cb > ob) {
252
- const sel = line.slice(0, ob).trim();
253
- const inner = line.slice(ob + 1, cb).trim();
254
- const full = resolveSelector(sel, curSel());
255
- if (!flatRules.has(full)) flatRules.set(full, []);
256
- if (inner) expandDecls(inner).forEach(d => addDecl(full, d));
257
- continue;
258
- }
259
-
260
- // ── Block opener: selector { ─────────────────────────────────────────────
261
- if (ob !== -1) {
262
- const sel = line.slice(0, ob).trim();
263
- const full = resolveSelector(sel, curSel());
264
- selStack.push(full);
265
- if (!flatRules.has(full)) flatRules.set(full, []);
266
- // Handle any declarations on the same line after {
267
- const tail = line.slice(ob + 1).trim();
268
- if (tail) expandDecls(tail).forEach(d => addDecl(full, d));
269
- continue;
270
- }
271
-
272
- // ── Closing brace ────────────────────────────────────────────────────────
273
- if (line === '}' || line === '};') {
274
- selStack.pop();
275
- continue;
276
- }
277
-
278
- // ── Declaration or boolean shorthand inside a rule block ─────────────────
279
- if (selStack.length > 0) {
280
- addDeclarationLine(line, curSel());
281
- }
282
- }
283
-
284
- // ── Emit flat rules ───────────────────────────────────────────────────────
285
- let css = '';
286
- for (const [sel, decls] of flatRules) {
287
- if (decls.length === 0) continue;
288
- css += `${ind}${sel} {\n`;
289
- for (const d of decls) css += `${ind1}${d};\n`;
290
- css += `${ind}}\n`;
291
- }
292
- // ── Emit at-rule blocks (after flat rules) ────────────────────────────────
293
- for (const seg of atRuleSegs) css += seg;
294
-
295
- return css;
296
- }
297
-
298
- // ── Main source preprocessor: replace css { } with CSS string ──────────────
299
- class CssPreprocessor {
300
- constructor(src) {
301
- this.src = src;
302
- this.pos = 0;
303
- this.out = '';
304
- }
305
-
306
- transform() {
307
- while (this.pos < this.src.length) {
308
- this.scan();
309
- }
310
- return this.out;
311
- }
312
-
313
- scan() {
314
- const c = this.src[this.pos];
315
-
316
- // Line comment — pass through unchanged
317
- if (c === '/' && this.src[this.pos + 1] === '/') {
318
- const end = this.src.indexOf('\n', this.pos);
319
- if (end === -1) { this.out += this.src.slice(this.pos); this.pos = this.src.length; }
320
- else { this.out += this.src.slice(this.pos, end + 1); this.pos = end + 1; }
321
- return;
322
- }
323
-
324
- // Block comment — pass through
325
- if (c === '/' && this.src[this.pos + 1] === '*') {
326
- const end = this.src.indexOf('*/', this.pos + 2);
327
- if (end === -1) { this.out += this.src.slice(this.pos); this.pos = this.src.length; }
328
- else { this.out += this.src.slice(this.pos, end + 2); this.pos = end + 2; }
329
- return;
330
- }
331
-
332
- // String literals — pass through
333
- if (c === '"' || c === "'" || c === '`') {
334
- this.passString(c);
335
- return;
336
- }
337
-
338
- // Detect standalone css keyword followed (across whitespace) by {
339
- if (c === 'c' && this.src.slice(this.pos, this.pos + 3) === 'css') {
340
- const charBefore = this.pos > 0 ? this.src[this.pos - 1] : '\n';
341
- const charAfter = this.src[this.pos + 3] || '';
342
- // Must not be part of a longer identifier
343
- if (!/[a-zA-Z0-9_]/.test(charBefore) && !/[a-zA-Z0-9_]/.test(charAfter)) {
344
- let j = this.pos + 3;
345
- // Skip whitespace/newlines looking for {
346
- while (j < this.src.length &&
347
- (this.src[j] === ' ' || this.src[j] === '\t' ||
348
- this.src[j] === '\n' || this.src[j] === '\r')) j++;
349
-
350
- if (this.src[j] === '{') {
351
- j++; // consume opening {
352
- let depth = 1;
353
- let blockStart = j;
354
-
355
- // Find matching closing }
356
- while (j < this.src.length && depth > 0) {
357
- const ch = this.src[j];
358
- if (ch === '"' || ch === "'" || ch === '`') {
359
- const q = ch; j++;
360
- while (j < this.src.length && this.src[j] !== q) {
361
- if (this.src[j] === '\\') j++;
362
- j++;
363
- }
364
- j++;
365
- } else if (ch === '{') { depth++; j++; }
366
- else if (ch === '}') { depth--; j++; }
367
- else j++;
368
- }
369
-
370
- const blockContent = this.src.slice(blockStart, j - 1);
371
- const css = parseCssBlockContent(blockContent);
372
- // Emit as backtick string (Flux raw multiline string, no interpolation)
373
- this.out += '`' + css.replace(/\\/g, '\\\\').replace(/`/g, '\\`') + '`';
374
- this.pos = j;
375
- return;
376
- }
377
- }
378
- }
379
-
380
- this.out += c;
381
- this.pos++;
382
- }
383
-
384
- passString(quote) {
385
- this.out += quote; this.pos++;
386
- while (this.pos < this.src.length) {
387
- const c = this.src[this.pos];
388
- if (c === '\\') { this.out += c + (this.src[this.pos + 1] || ''); this.pos += 2; continue; }
389
- if (c === quote) { this.out += c; this.pos++; return; }
390
- this.out += c; this.pos++;
391
- }
392
- }
393
- }
394
-
395
- function transformCss(src) {
396
- return new CssPreprocessor(src).transform();
397
- }
398
-
399
- module.exports = { transformCss, parseCssBlockContent, CSS_PROP_MAP, CSS_BOOL_MAP };