@xaendar/cli 0.2.1 → 0.3.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/index.js +219 -1264
- package/index.js.map +1 -1
- package/package.json +3 -3
package/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
const require = createRequire(import.meta.url);
|
|
2
4
|
var __create = Object.create;
|
|
3
5
|
var __defProp = Object.defineProperty;
|
|
4
6
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -6,7 +8,7 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
|
6
8
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
9
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
10
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
9
|
-
get: (
|
|
11
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
10
12
|
}) : x)(function(x) {
|
|
11
13
|
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
12
14
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
@@ -230,8 +232,8 @@ var require_help = __commonJS({
|
|
|
230
232
|
visibleCommands.push(helpCommand);
|
|
231
233
|
}
|
|
232
234
|
if (this.sortSubcommands) {
|
|
233
|
-
visibleCommands.sort((
|
|
234
|
-
return
|
|
235
|
+
visibleCommands.sort((a, b) => {
|
|
236
|
+
return a.name().localeCompare(b.name());
|
|
235
237
|
});
|
|
236
238
|
}
|
|
237
239
|
return visibleCommands;
|
|
@@ -243,11 +245,11 @@ var require_help = __commonJS({
|
|
|
243
245
|
* @param {Option} b
|
|
244
246
|
* @returns {number}
|
|
245
247
|
*/
|
|
246
|
-
compareOptions(
|
|
248
|
+
compareOptions(a, b) {
|
|
247
249
|
const getSortKey = (option) => {
|
|
248
250
|
return option.short ? option.short.replace(/^-/, "") : option.long.replace(/^--/, "");
|
|
249
251
|
};
|
|
250
|
-
return getSortKey(
|
|
252
|
+
return getSortKey(a).localeCompare(getSortKey(b));
|
|
251
253
|
}
|
|
252
254
|
/**
|
|
253
255
|
* Get an array of the visible options. Includes a placeholder for the implicit help option, if there is one.
|
|
@@ -1113,20 +1115,20 @@ var require_option = __commonJS({
|
|
|
1113
1115
|
var require_suggestSimilar = __commonJS({
|
|
1114
1116
|
"../node_modules/commander/lib/suggestSimilar.js"(exports) {
|
|
1115
1117
|
var maxDistance = 3;
|
|
1116
|
-
function editDistance(
|
|
1117
|
-
if (Math.abs(
|
|
1118
|
-
return Math.max(
|
|
1118
|
+
function editDistance(a, b) {
|
|
1119
|
+
if (Math.abs(a.length - b.length) > maxDistance)
|
|
1120
|
+
return Math.max(a.length, b.length);
|
|
1119
1121
|
const d = [];
|
|
1120
|
-
for (let i = 0; i <=
|
|
1122
|
+
for (let i = 0; i <= a.length; i++) {
|
|
1121
1123
|
d[i] = [i];
|
|
1122
1124
|
}
|
|
1123
1125
|
for (let j = 0; j <= b.length; j++) {
|
|
1124
1126
|
d[0][j] = j;
|
|
1125
1127
|
}
|
|
1126
1128
|
for (let j = 1; j <= b.length; j++) {
|
|
1127
|
-
for (let i = 1; i <=
|
|
1129
|
+
for (let i = 1; i <= a.length; i++) {
|
|
1128
1130
|
let cost = 1;
|
|
1129
|
-
if (
|
|
1131
|
+
if (a[i - 1] === b[j - 1]) {
|
|
1130
1132
|
cost = 0;
|
|
1131
1133
|
} else {
|
|
1132
1134
|
cost = 1;
|
|
@@ -1139,12 +1141,12 @@ var require_suggestSimilar = __commonJS({
|
|
|
1139
1141
|
d[i - 1][j - 1] + cost
|
|
1140
1142
|
// substitution
|
|
1141
1143
|
);
|
|
1142
|
-
if (i > 1 && j > 1 &&
|
|
1144
|
+
if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
|
|
1143
1145
|
d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1);
|
|
1144
1146
|
}
|
|
1145
1147
|
}
|
|
1146
1148
|
}
|
|
1147
|
-
return d[
|
|
1149
|
+
return d[a.length][b.length];
|
|
1148
1150
|
}
|
|
1149
1151
|
function suggestSimilar(word, candidates) {
|
|
1150
1152
|
if (!candidates || candidates.length === 0) return "";
|
|
@@ -1171,7 +1173,7 @@ var require_suggestSimilar = __commonJS({
|
|
|
1171
1173
|
}
|
|
1172
1174
|
}
|
|
1173
1175
|
});
|
|
1174
|
-
similar.sort((
|
|
1176
|
+
similar.sort((a, b) => a.localeCompare(b));
|
|
1175
1177
|
if (searchingOptions) {
|
|
1176
1178
|
similar = similar.map((candidate) => `--${candidate}`);
|
|
1177
1179
|
}
|
|
@@ -3477,8 +3479,9 @@ var {
|
|
|
3477
3479
|
// ../packages/cli/src/commands/generate/component/component.command.ts
|
|
3478
3480
|
import { mkdirSync, writeFileSync, existsSync, rmSync } from "fs";
|
|
3479
3481
|
import { join } from "path";
|
|
3480
|
-
function generateComponent(name, path, force) {
|
|
3482
|
+
function generateComponent(name, path, force, style) {
|
|
3481
3483
|
const dir = join(path, name);
|
|
3484
|
+
style ??= "css";
|
|
3482
3485
|
if (existsSync(dir)) {
|
|
3483
3486
|
if (force) {
|
|
3484
3487
|
console.log(`Deleting "${name}"...`);
|
|
@@ -3491,9 +3494,9 @@ function generateComponent(name, path, force) {
|
|
|
3491
3494
|
}
|
|
3492
3495
|
mkdirSync(dir, { recursive: true });
|
|
3493
3496
|
const files = [
|
|
3494
|
-
[`${name}.xd.component.ts`, tsTemplate(name)],
|
|
3497
|
+
[`${name}.xd.component.ts`, tsTemplate(name, style)],
|
|
3495
3498
|
[`${name}.xd.component.html`, htmlTemplate(name)],
|
|
3496
|
-
[`${name}.xd.component
|
|
3499
|
+
[`${name}.xd.component.${style}`, cssTemplate(name)],
|
|
3497
3500
|
[`${name}.xd.component.spec.ts`, specTemplate(name)]
|
|
3498
3501
|
];
|
|
3499
3502
|
for (const [filename, content] of files) {
|
|
@@ -3505,13 +3508,13 @@ function generateComponent(name, path, force) {
|
|
|
3505
3508
|
console.log(`${name}/${filename}`);
|
|
3506
3509
|
}
|
|
3507
3510
|
}
|
|
3508
|
-
function tsTemplate(name) {
|
|
3511
|
+
function tsTemplate(name, style) {
|
|
3509
3512
|
const className = toPascalCase(name);
|
|
3510
3513
|
return `import { BaseWebComponent, WebComponent } from '@xaendar/core';
|
|
3511
3514
|
|
|
3512
3515
|
@WebComponent({
|
|
3513
3516
|
selector: '${toKebabCase(name)}',
|
|
3514
|
-
styleUrl: './${name}.xd.component
|
|
3517
|
+
styleUrl: './${name}.xd.component.${style}',
|
|
3515
3518
|
templateUrl: './${name}.xd.component.html'
|
|
3516
3519
|
})
|
|
3517
3520
|
export class ${className}Component extends BaseWebComponent {
|
|
@@ -3528,8 +3531,7 @@ function cssTemplate(_name) {
|
|
|
3528
3531
|
}
|
|
3529
3532
|
function specTemplate(name) {
|
|
3530
3533
|
const className = toPascalCase(name);
|
|
3531
|
-
return `
|
|
3532
|
-
import { describe, expect, it } from "vitest";
|
|
3534
|
+
return `import { describe, expect, it } from "vitest";
|
|
3533
3535
|
import { ${className}Component } from './${name}.xd.component';
|
|
3534
3536
|
|
|
3535
3537
|
describe('${className}Component', () => {
|
|
@@ -3548,8 +3550,8 @@ function toKebabCase(str) {
|
|
|
3548
3550
|
}
|
|
3549
3551
|
|
|
3550
3552
|
// ../packages/cli/src/commands/generate/generate.command.ts
|
|
3551
|
-
function
|
|
3552
|
-
const generate = new Command("generate").alias("g").description("Generate
|
|
3553
|
+
function generateCommand() {
|
|
3554
|
+
const generate = new Command("generate").alias("g").description("Generate Xaendar building blocks");
|
|
3553
3555
|
generate.command("component <name>").alias("c").option("-p, --path <path>", "Custom path for the generated component (default: current directory)").option("-f, --force", "Force generation deleting current component if already exists").description("Generate a new component (creates <name>/ folder with .ts, .html, .css, .spec.ts)").action((name, options) => {
|
|
3554
3556
|
const path = options.path || process.cwd();
|
|
3555
3557
|
generateComponent(name, path, !!options.force);
|
|
@@ -3557,1286 +3559,239 @@ function makeGenerateCommand() {
|
|
|
3557
3559
|
return generate;
|
|
3558
3560
|
}
|
|
3559
3561
|
|
|
3560
|
-
// ../packages/
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
* Internal array holding the stack elements, ordered from bottom to top.
|
|
3564
|
-
*/
|
|
3565
|
-
_elements = new Array();
|
|
3566
|
-
constructor() {
|
|
3567
|
-
return new Proxy(this, {
|
|
3568
|
-
get(target, prop) {
|
|
3569
|
-
return isNaN(Number(prop)) ? target[prop] : target._elements[Number(prop)];
|
|
3570
|
-
}
|
|
3571
|
-
});
|
|
3572
|
-
}
|
|
3573
|
-
/**
|
|
3574
|
-
* The number of elements currently in the stack.
|
|
3575
|
-
*/
|
|
3576
|
-
get length() {
|
|
3577
|
-
return this._elements.length;
|
|
3578
|
-
}
|
|
3579
|
-
/**
|
|
3580
|
-
* A shallow copy of the elements in the stack, ordered from bottom to top.
|
|
3581
|
-
*/
|
|
3582
|
-
get values() {
|
|
3583
|
-
return [...this._elements];
|
|
3584
|
-
}
|
|
3585
|
-
/**
|
|
3586
|
-
* Removes and returns the top element of the stack.
|
|
3587
|
-
*
|
|
3588
|
-
* @returns The top element, or `undefined` if the stack is empty.
|
|
3589
|
-
*/
|
|
3590
|
-
pop() {
|
|
3591
|
-
return this._elements.pop();
|
|
3592
|
-
}
|
|
3593
|
-
/**
|
|
3594
|
-
* Pushes an element onto the top of the stack.
|
|
3595
|
-
*
|
|
3596
|
-
* @param element - The element to push.
|
|
3597
|
-
* @returns The new length of the stack.
|
|
3598
|
-
*/
|
|
3599
|
-
push(element) {
|
|
3600
|
-
return this._elements.push(element);
|
|
3601
|
-
}
|
|
3602
|
-
};
|
|
3603
|
-
|
|
3604
|
-
// ../packages/compiler/src/costants/chars.constants.ts
|
|
3605
|
-
var EOF = 0;
|
|
3606
|
-
var LF = 10;
|
|
3607
|
-
var CR = 13;
|
|
3608
|
-
var SPACE = 32;
|
|
3609
|
-
var LPAREN = 40;
|
|
3610
|
-
var RPAREN = 41;
|
|
3611
|
-
var SLASH = 47;
|
|
3612
|
-
var LESS_THAN = 60;
|
|
3613
|
-
var GREATER_THEN = 62;
|
|
3614
|
-
var AT_SIGN = 64;
|
|
3615
|
-
var GRAVE_ACCENT = 96;
|
|
3616
|
-
var LEFT_BRACE = 123;
|
|
3617
|
-
var RIGHT_BRACE = 125;
|
|
3562
|
+
// ../packages/cli/src/commands/new/new.command.ts
|
|
3563
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, writeFileSync as writeFileSync2 } from "fs";
|
|
3564
|
+
import { resolve } from "path/win32";
|
|
3618
3565
|
|
|
3619
|
-
// ../packages/
|
|
3620
|
-
|
|
3621
|
-
/**
|
|
3622
|
-
* Creates a new cursor for the given input source.
|
|
3623
|
-
*
|
|
3624
|
-
* @param input Full source string to be tokenized.
|
|
3625
|
-
*/
|
|
3626
|
-
constructor(input) {
|
|
3627
|
-
this.input = input;
|
|
3628
|
-
}
|
|
3629
|
-
input;
|
|
3630
|
-
/**
|
|
3631
|
-
* Representation of the current character.
|
|
3632
|
-
*
|
|
3633
|
-
* - `index`: absolute index within the input string
|
|
3634
|
-
* - `code`: Unicode code point of the character
|
|
3635
|
-
* - `value`: actual character value
|
|
3636
|
-
*
|
|
3637
|
-
* An index of `-1` indicates that the cursor has not yet consumed
|
|
3638
|
-
* any character or has reached EOF.
|
|
3639
|
-
*/
|
|
3640
|
-
_currentChar = {
|
|
3641
|
-
code: 0,
|
|
3642
|
-
index: -1,
|
|
3643
|
-
value: ""
|
|
3644
|
-
};
|
|
3645
|
-
/**
|
|
3646
|
-
* Returns a read-only snapshot of the current character.
|
|
3647
|
-
*/
|
|
3648
|
-
get currentChar() {
|
|
3649
|
-
return this._currentChar;
|
|
3650
|
-
}
|
|
3651
|
-
/**
|
|
3652
|
-
* Cache used by peek operations to avoid re-reading
|
|
3653
|
-
* the same character positions multiple times.
|
|
3654
|
-
*
|
|
3655
|
-
* Key: absolute character index
|
|
3656
|
-
* Value: Unicode code point
|
|
3657
|
-
*/
|
|
3658
|
-
_peekCache = /* @__PURE__ */ new Map();
|
|
3659
|
-
/**
|
|
3660
|
-
* Logical position of the cursor in the input.
|
|
3661
|
-
*
|
|
3662
|
-
* - `row`: zero-based line number
|
|
3663
|
-
* - `column`: zero-based column number
|
|
3664
|
-
*/
|
|
3665
|
-
_position = {
|
|
3666
|
-
row: 0,
|
|
3667
|
-
column: 0
|
|
3668
|
-
};
|
|
3669
|
-
/**
|
|
3670
|
-
* Returns a read-only snapshot of the current cursor position.
|
|
3671
|
-
*/
|
|
3672
|
-
get position() {
|
|
3673
|
-
return this._position;
|
|
3674
|
-
}
|
|
3675
|
-
/**
|
|
3676
|
-
* Advances the cursor by the specified number of characters.
|
|
3677
|
-
*
|
|
3678
|
-
* This method:
|
|
3679
|
-
* - Updates the current character
|
|
3680
|
-
* - Updates row/column position
|
|
3681
|
-
* - Detects line breaks (LF / CR)
|
|
3682
|
-
* - Throws an EOF error when the end of the input is reached
|
|
3683
|
-
*
|
|
3684
|
-
* @param chars Number of characters to consume (must be >= 1)
|
|
3685
|
-
*
|
|
3686
|
-
* @throws Error with cause `EOF` when advancing past input length
|
|
3687
|
-
*/
|
|
3688
|
-
advance(chars = 1) {
|
|
3689
|
-
if (chars < 1) {
|
|
3690
|
-
throw new Error(`${chars} is not a valid value. Please enter a number equal or greater than 1`);
|
|
3691
|
-
}
|
|
3692
|
-
const newIndex = this._currentChar.index + chars;
|
|
3693
|
-
if (newIndex >= this.input.length) {
|
|
3694
|
-
this._currentChar.code = EOF;
|
|
3695
|
-
this._currentChar.index = -1;
|
|
3696
|
-
this._currentChar.value = "";
|
|
3697
|
-
this.throwEOFError();
|
|
3698
|
-
} else {
|
|
3699
|
-
if ([LF, CR].includes(this._currentChar.code)) {
|
|
3700
|
-
this._position.row++;
|
|
3701
|
-
this._position.column = 0;
|
|
3702
|
-
} else {
|
|
3703
|
-
this._position.column++;
|
|
3704
|
-
}
|
|
3705
|
-
this._currentChar.index = newIndex;
|
|
3706
|
-
this._currentChar.value = this.input[newIndex];
|
|
3707
|
-
this._currentChar.code = this.input.charCodeAt(newIndex);
|
|
3708
|
-
}
|
|
3709
|
-
}
|
|
3710
|
-
peekMatch(pattern, length) {
|
|
3711
|
-
if (typeof pattern === "string") {
|
|
3712
|
-
const peekedChars = this.peek(pattern.length);
|
|
3713
|
-
for (let i = 0; i < pattern.length; i++) {
|
|
3714
|
-
if (peekedChars[i] !== pattern.charCodeAt(i)) {
|
|
3715
|
-
return false;
|
|
3716
|
-
}
|
|
3717
|
-
}
|
|
3718
|
-
return true;
|
|
3719
|
-
}
|
|
3720
|
-
const start = this._currentChar.index + 1;
|
|
3721
|
-
const slice = this.input.slice(start, start + length);
|
|
3722
|
-
return pattern.test(slice);
|
|
3723
|
-
}
|
|
3724
|
-
peek(charsOrOptions, options) {
|
|
3725
|
-
const cache = this._peekCache;
|
|
3726
|
-
const chars = typeof charsOrOptions === "number" ? charsOrOptions : 1;
|
|
3727
|
-
const offset = (typeof charsOrOptions === "object" ? charsOrOptions : options)?.offset ?? 0;
|
|
3728
|
-
return chars === 1 ? this.peekOneChar(this._currentChar.index + offset + 1, cache) : this.peekMany(chars + offset, cache);
|
|
3729
|
-
}
|
|
3730
|
-
/**
|
|
3731
|
-
* Skips all consecutive space characters from the current position.
|
|
3732
|
-
*/
|
|
3733
|
-
skipSpaces() {
|
|
3734
|
-
while (this.peek() === SPACE) {
|
|
3735
|
-
this.advance();
|
|
3736
|
-
}
|
|
3737
|
-
}
|
|
3738
|
-
/**
|
|
3739
|
-
* Peeks multiple characters ahead.
|
|
3740
|
-
*/
|
|
3741
|
-
peekMany(chars, cache) {
|
|
3742
|
-
const peekedChars = new Array();
|
|
3743
|
-
const nextCharIndex = this._currentChar.index + 1;
|
|
3744
|
-
for (let i = nextCharIndex; i < nextCharIndex + chars; i++) {
|
|
3745
|
-
peekedChars.push(this.peekOneChar(i, cache));
|
|
3746
|
-
}
|
|
3747
|
-
return peekedChars;
|
|
3748
|
-
}
|
|
3749
|
-
/**
|
|
3750
|
-
* Peeks a single character at the given absolute index.
|
|
3751
|
-
*/
|
|
3752
|
-
peekOneChar(index, cache) {
|
|
3753
|
-
if (cache.has(index)) {
|
|
3754
|
-
return cache.get(index);
|
|
3755
|
-
}
|
|
3756
|
-
if (index >= this.input.length) {
|
|
3757
|
-
this.throwEOFError();
|
|
3758
|
-
}
|
|
3759
|
-
const charCode = this.input.charCodeAt(index);
|
|
3760
|
-
cache.set(index, charCode);
|
|
3761
|
-
return charCode;
|
|
3762
|
-
}
|
|
3763
|
-
/**
|
|
3764
|
-
* Throws a standardized EOF error used by the lexer engine
|
|
3765
|
-
* to terminate tokenization.
|
|
3766
|
-
*/
|
|
3767
|
-
throwEOFError() {
|
|
3768
|
-
throw new Error("", { cause: EOF });
|
|
3769
|
-
}
|
|
3770
|
-
};
|
|
3566
|
+
// ../packages/cli/src/commands/new/structure.ts
|
|
3567
|
+
import { readFileSync } from "fs";
|
|
3771
3568
|
|
|
3772
|
-
// ../packages/
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
TokenType2[TokenType2["DEFAULT"] = 14] = "DEFAULT";
|
|
3789
|
-
TokenType2[TokenType2["CONDITION"] = 15] = "CONDITION";
|
|
3790
|
-
TokenType2[TokenType2["BLOCK_OPEN"] = 16] = "BLOCK_OPEN";
|
|
3791
|
-
TokenType2[TokenType2["BLOCK_CLOSE"] = 17] = "BLOCK_CLOSE";
|
|
3792
|
-
TokenType2[TokenType2["EOF"] = 18] = "EOF";
|
|
3793
|
-
return TokenType2;
|
|
3794
|
-
})(TokenType || {});
|
|
3795
|
-
|
|
3796
|
-
// ../packages/compiler/src/lexer/states/attribute.state.ts
|
|
3797
|
-
function consumeAttribute(cursor, _context) {
|
|
3798
|
-
let read = true;
|
|
3799
|
-
let attribute = "";
|
|
3800
|
-
let retVal;
|
|
3801
|
-
while (read) {
|
|
3802
|
-
switch (cursor.peek()) {
|
|
3803
|
-
case SPACE:
|
|
3804
|
-
case SLASH:
|
|
3805
|
-
case GREATER_THEN:
|
|
3806
|
-
retVal = {
|
|
3807
|
-
state: "tag-body" /* TAG_BODY */,
|
|
3808
|
-
tokens: [{
|
|
3809
|
-
type: 5 /* ATTRIBUTE */,
|
|
3810
|
-
parts: [attribute]
|
|
3811
|
-
}]
|
|
3812
|
-
};
|
|
3813
|
-
read = false;
|
|
3814
|
-
break;
|
|
3815
|
-
case LEFT_BRACE:
|
|
3816
|
-
retVal = {
|
|
3817
|
-
state: "interpolation" /* INTERPOLATION */,
|
|
3818
|
-
tokens: [{
|
|
3819
|
-
type: 5 /* ATTRIBUTE */,
|
|
3820
|
-
parts: [attribute]
|
|
3821
|
-
}],
|
|
3822
|
-
pushState: true
|
|
3823
|
-
};
|
|
3824
|
-
read = false;
|
|
3825
|
-
break;
|
|
3826
|
-
default:
|
|
3827
|
-
cursor.advance();
|
|
3828
|
-
attribute = `${attribute}${cursor.currentChar.value}`;
|
|
3829
|
-
}
|
|
3830
|
-
}
|
|
3831
|
-
return retVal;
|
|
3832
|
-
}
|
|
3833
|
-
|
|
3834
|
-
// ../packages/compiler/src/lexer/states/event.state.ts
|
|
3835
|
-
function consumeEvent(cursor, _context) {
|
|
3836
|
-
let read = true;
|
|
3837
|
-
let event = "";
|
|
3838
|
-
let retVal;
|
|
3839
|
-
cursor.advance();
|
|
3840
|
-
while (read) {
|
|
3841
|
-
switch (cursor.peek()) {
|
|
3842
|
-
case SPACE:
|
|
3843
|
-
case SLASH:
|
|
3844
|
-
case GREATER_THEN:
|
|
3845
|
-
retVal = {
|
|
3846
|
-
state: "tag-body" /* TAG_BODY */,
|
|
3847
|
-
tokens: [{
|
|
3848
|
-
type: 6 /* EVENT */,
|
|
3849
|
-
parts: [event]
|
|
3850
|
-
}]
|
|
3851
|
-
};
|
|
3852
|
-
read = false;
|
|
3853
|
-
break;
|
|
3854
|
-
default:
|
|
3855
|
-
cursor.advance();
|
|
3856
|
-
event = `${event}${cursor.currentChar.value}`;
|
|
3857
|
-
}
|
|
3858
|
-
}
|
|
3859
|
-
return retVal;
|
|
3860
|
-
}
|
|
3861
|
-
|
|
3862
|
-
// ../packages/compiler/src/lexer/states/flow-control.ts
|
|
3863
|
-
function consumeFlowControl(cursor, _context) {
|
|
3864
|
-
let retVal;
|
|
3865
|
-
cursor.advance();
|
|
3866
|
-
if (cursor.peekMatch("for ")) {
|
|
3867
|
-
cursor.advance(4);
|
|
3868
|
-
retVal = {
|
|
3869
|
-
state: "flow-control-condition" /* FLOW_CONTROL_CONDITION */,
|
|
3870
|
-
tokens: [{
|
|
3871
|
-
type: 10 /* FOR */
|
|
3872
|
-
}],
|
|
3873
|
-
pushState: true
|
|
3874
|
-
};
|
|
3875
|
-
} else if (cursor.peekMatch("if ")) {
|
|
3876
|
-
cursor.advance(2);
|
|
3877
|
-
retVal = {
|
|
3878
|
-
state: "flow-control-condition" /* FLOW_CONTROL_CONDITION */,
|
|
3879
|
-
tokens: [{
|
|
3880
|
-
type: 9 /* IF */
|
|
3881
|
-
}],
|
|
3882
|
-
pushState: true
|
|
3883
|
-
};
|
|
3884
|
-
} else if (cursor.peekMatch("else ")) {
|
|
3885
|
-
cursor.advance(5);
|
|
3886
|
-
retVal = {
|
|
3887
|
-
state: "flow-control-block" /* FLOW_CONTROL_BLOCK */,
|
|
3888
|
-
tokens: [{
|
|
3889
|
-
type: 11 /* ELSE */
|
|
3890
|
-
}],
|
|
3891
|
-
pushState: true
|
|
3892
|
-
};
|
|
3893
|
-
} else if (cursor.peekMatch("switch ")) {
|
|
3894
|
-
cursor.advance(7);
|
|
3895
|
-
retVal = {
|
|
3896
|
-
state: "flow-control-condition" /* FLOW_CONTROL_CONDITION */,
|
|
3897
|
-
tokens: [{
|
|
3898
|
-
type: 12 /* SWITCH */
|
|
3899
|
-
}],
|
|
3900
|
-
pushState: true
|
|
3901
|
-
};
|
|
3902
|
-
} else if (cursor.peekMatch("case ")) {
|
|
3903
|
-
cursor.advance(5);
|
|
3904
|
-
retVal = {
|
|
3905
|
-
state: "flow-control-condition" /* FLOW_CONTROL_CONDITION */,
|
|
3906
|
-
tokens: [{
|
|
3907
|
-
type: 13 /* CASE */
|
|
3908
|
-
}],
|
|
3909
|
-
pushState: true
|
|
3910
|
-
};
|
|
3911
|
-
} else if (cursor.peekMatch("default ")) {
|
|
3912
|
-
cursor.advance(8);
|
|
3913
|
-
retVal = {
|
|
3914
|
-
state: "flow-control-block" /* FLOW_CONTROL_BLOCK */,
|
|
3915
|
-
tokens: [{
|
|
3916
|
-
type: 14 /* DEFAULT */
|
|
3917
|
-
}],
|
|
3918
|
-
pushState: true
|
|
3919
|
-
};
|
|
3920
|
-
}
|
|
3921
|
-
return retVal;
|
|
3922
|
-
}
|
|
3923
|
-
|
|
3924
|
-
// ../packages/compiler/src/lexer/states/flow-control-block.state.ts
|
|
3925
|
-
function consumeFlowControlBlock(cursor, _context) {
|
|
3926
|
-
cursor.skipSpaces();
|
|
3927
|
-
if (cursor.peek() !== LEFT_BRACE) {
|
|
3928
|
-
throw new Error(`Expected '{' but got '${String.fromCharCode(cursor.peek())}' at row ${cursor.position.row}, col ${cursor.position.column}`);
|
|
3929
|
-
}
|
|
3930
|
-
cursor.advance();
|
|
3931
|
-
return {
|
|
3932
|
-
state: "text" /* TEXT */,
|
|
3933
|
-
tokens: [{ type: 16 /* BLOCK_OPEN */ }],
|
|
3934
|
-
pushState: true
|
|
3935
|
-
};
|
|
3569
|
+
// ../packages/cli/src/commands/new/templates/index-html.ts
|
|
3570
|
+
function indexHtml(name) {
|
|
3571
|
+
const selector = `${name}-root`;
|
|
3572
|
+
return `<!DOCTYPE html>
|
|
3573
|
+
<html lang="en">
|
|
3574
|
+
<head>
|
|
3575
|
+
<meta charset="UTF-8" />
|
|
3576
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
3577
|
+
<title>${name}</title>
|
|
3578
|
+
</head>
|
|
3579
|
+
<body>
|
|
3580
|
+
<${selector}></${selector}>
|
|
3581
|
+
<script type="module" src="/src/main.ts"></script>
|
|
3582
|
+
</body>
|
|
3583
|
+
</html>
|
|
3584
|
+
`;
|
|
3936
3585
|
}
|
|
3937
3586
|
|
|
3938
|
-
// ../packages/
|
|
3939
|
-
function
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
cursor.advance();
|
|
3945
|
-
let expression = "";
|
|
3946
|
-
let depth = 1;
|
|
3947
|
-
while (depth > 0) {
|
|
3948
|
-
const code = cursor.peek();
|
|
3949
|
-
switch (code) {
|
|
3950
|
-
case LPAREN:
|
|
3951
|
-
depth++;
|
|
3952
|
-
cursor.advance();
|
|
3953
|
-
break;
|
|
3954
|
-
case RPAREN:
|
|
3955
|
-
depth--;
|
|
3956
|
-
if (!depth) {
|
|
3957
|
-
cursor.advance();
|
|
3958
|
-
break;
|
|
3959
|
-
}
|
|
3960
|
-
cursor.advance();
|
|
3961
|
-
break;
|
|
3962
|
-
default:
|
|
3963
|
-
cursor.advance();
|
|
3964
|
-
expression = `${expression}${cursor.currentChar.value}`;
|
|
3965
|
-
}
|
|
3966
|
-
}
|
|
3967
|
-
return {
|
|
3968
|
-
state: "flow-control-block" /* FLOW_CONTROL_BLOCK */,
|
|
3969
|
-
tokens: [{
|
|
3970
|
-
type: 15 /* CONDITION */,
|
|
3971
|
-
parts: [expression]
|
|
3972
|
-
}],
|
|
3973
|
-
popState: true
|
|
3974
|
-
};
|
|
3587
|
+
// ../packages/cli/src/commands/new/templates/main-ts.ts
|
|
3588
|
+
function mainTs(componentName) {
|
|
3589
|
+
return `import { loadSignals } from '@xaendar/signals';
|
|
3590
|
+
|
|
3591
|
+
loadSignals();
|
|
3592
|
+
`;
|
|
3975
3593
|
}
|
|
3976
3594
|
|
|
3977
|
-
// ../packages/
|
|
3978
|
-
function
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
}
|
|
4002
|
-
;
|
|
4003
|
-
retVal = {
|
|
4004
|
-
state,
|
|
4005
|
-
tokens: [{
|
|
4006
|
-
type: 8 /* INTERPOLATION_EXPRESSION */,
|
|
4007
|
-
parts: [interpolation]
|
|
4008
|
-
}],
|
|
4009
|
-
popState: true
|
|
4010
|
-
};
|
|
4011
|
-
read = false;
|
|
4012
|
-
} else {
|
|
4013
|
-
interpolation = addCharacter(cursor, interpolation);
|
|
4014
|
-
}
|
|
4015
|
-
break;
|
|
4016
|
-
default:
|
|
4017
|
-
interpolation = addCharacter(cursor, interpolation);
|
|
4018
|
-
}
|
|
3595
|
+
// ../packages/cli/src/commands/new/templates/package-json.ts
|
|
3596
|
+
function packageJson(name, version) {
|
|
3597
|
+
return `{
|
|
3598
|
+
"name": "${name}",
|
|
3599
|
+
"version": "0.0.1",
|
|
3600
|
+
"private": true,
|
|
3601
|
+
"scripts": {
|
|
3602
|
+
"build": "vite build",
|
|
3603
|
+
"start": "vite",
|
|
3604
|
+
"test": "vitest",
|
|
3605
|
+
"xd": "xd"
|
|
3606
|
+
},
|
|
3607
|
+
"dependencies": {
|
|
3608
|
+
"@xaendar/core": "^${version}",
|
|
3609
|
+
"@xaendar/signals": "^${version}",
|
|
3610
|
+
"@xaendar/types": "^${version}"
|
|
3611
|
+
},
|
|
3612
|
+
"devDependencies": {
|
|
3613
|
+
"@vitest/coverage-v8": "^4.1.7",
|
|
3614
|
+
"@xaendar/build-tools": "^${version}",
|
|
3615
|
+
"@xaendar/cli": "^${version}",
|
|
3616
|
+
"typescript": "^6.0.3",
|
|
3617
|
+
"vite": "^8.0.14",
|
|
3618
|
+
"vitest": "^4.1.7"
|
|
4019
3619
|
}
|
|
4020
|
-
return retVal;
|
|
4021
3620
|
}
|
|
4022
|
-
|
|
4023
|
-
cursor.advance(1);
|
|
4024
|
-
return `${interpolation}${cursor.currentChar.value}`;
|
|
3621
|
+
`;
|
|
4025
3622
|
}
|
|
4026
3623
|
|
|
4027
|
-
// ../packages/
|
|
4028
|
-
function
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
}
|
|
4048
|
-
;
|
|
4049
|
-
retVal = {
|
|
4050
|
-
state,
|
|
4051
|
-
tokens: [{
|
|
4052
|
-
type: 7 /* INTERPOLATION_LITERAL */,
|
|
4053
|
-
parts: [interpolation]
|
|
4054
|
-
}],
|
|
4055
|
-
popState: true
|
|
4056
|
-
};
|
|
4057
|
-
read = false;
|
|
4058
|
-
} else {
|
|
4059
|
-
interpolation = `${interpolation}${cursor.currentChar.value}`;
|
|
4060
|
-
}
|
|
4061
|
-
break;
|
|
4062
|
-
default:
|
|
4063
|
-
interpolation = addCharacter2(cursor, interpolation);
|
|
3624
|
+
// ../packages/cli/src/commands/new/templates/tsconfig-json.ts
|
|
3625
|
+
function tsconfigJson() {
|
|
3626
|
+
return `{
|
|
3627
|
+
"compilerOptions": {
|
|
3628
|
+
"target": "ESNext",
|
|
3629
|
+
"module": "ESNext",
|
|
3630
|
+
"moduleResolution": "bundler",
|
|
3631
|
+
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
|
3632
|
+
"outDir": "./dist",
|
|
3633
|
+
"rootDir": "./src",
|
|
3634
|
+
"declaration": true,
|
|
3635
|
+
"declarationMap": true,
|
|
3636
|
+
"sourceMap": true,
|
|
3637
|
+
"isolatedModules": true,
|
|
3638
|
+
"moduleDetection": "force",
|
|
3639
|
+
"noUncheckedIndexedAccess": true,
|
|
3640
|
+
"noEmit": true,
|
|
3641
|
+
"skipLibCheck": true,
|
|
3642
|
+
"paths": {
|
|
3643
|
+
"@/*": ["./src/*"]
|
|
4064
3644
|
}
|
|
4065
|
-
}
|
|
4066
|
-
|
|
3645
|
+
},
|
|
3646
|
+
"include": ["src/**/*.ts"],
|
|
3647
|
+
"exclude": ["dist", "node_modules", "**/*.spec.ts"]
|
|
4067
3648
|
}
|
|
4068
|
-
|
|
4069
|
-
cursor.advance(1);
|
|
4070
|
-
return `${interpolation}${cursor.currentChar.value}`;
|
|
3649
|
+
`;
|
|
4071
3650
|
}
|
|
4072
3651
|
|
|
4073
|
-
// ../packages/
|
|
4074
|
-
function
|
|
4075
|
-
return
|
|
4076
|
-
}
|
|
4077
|
-
function isJSIdentifierStart(code) {
|
|
4078
|
-
return code >= 65 && code <= 90 || // A-Z
|
|
4079
|
-
code >= 97 && code <= 122 || // a-z
|
|
4080
|
-
code === 36 || // $
|
|
4081
|
-
code === 95;
|
|
4082
|
-
}
|
|
3652
|
+
// ../packages/cli/src/commands/new/templates/vite-config-ts.ts
|
|
3653
|
+
function viteConfigTs() {
|
|
3654
|
+
return `import { defineConfig } from 'vite';
|
|
3655
|
+
import { xaendarPlugin } from '@xaendar/build-tools';
|
|
4083
3656
|
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
cursor.skipSpaces();
|
|
4089
|
-
const nextChar = cursor.peek();
|
|
4090
|
-
if (nextChar === GRAVE_ACCENT) {
|
|
4091
|
-
retVal = { state: "interpolation-literal" /* INTERPOLATION_LITERAL */ };
|
|
4092
|
-
} else if (isJSIdentifierStart(nextChar)) {
|
|
4093
|
-
retVal = { state: "interpolation-expression" /* INTERPOLATION_EXPRESSION */ };
|
|
4094
|
-
} else {
|
|
4095
|
-
throw new Error(`Unrecognized First Character ${String.fromCharCode(nextChar)} in interpolation`);
|
|
4096
|
-
}
|
|
4097
|
-
return retVal;
|
|
3657
|
+
export default defineConfig({
|
|
3658
|
+
plugins: [xaendarPlugin()],
|
|
3659
|
+
});
|
|
3660
|
+
`;
|
|
4098
3661
|
}
|
|
4099
3662
|
|
|
4100
|
-
// ../packages/
|
|
4101
|
-
function
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
};
|
|
4111
|
-
read = false;
|
|
4112
|
-
break;
|
|
4113
|
-
case SPACE:
|
|
4114
|
-
cursor.advance();
|
|
4115
|
-
break;
|
|
4116
|
-
case GREATER_THEN:
|
|
4117
|
-
case SLASH:
|
|
4118
|
-
retVal = {
|
|
4119
|
-
state: "tag-open-end" /* TAG_OPEN_END */
|
|
4120
|
-
};
|
|
4121
|
-
read = false;
|
|
4122
|
-
break;
|
|
4123
|
-
default:
|
|
4124
|
-
retVal = {
|
|
4125
|
-
state: "attribute" /* ATTRIBUTE */
|
|
4126
|
-
};
|
|
4127
|
-
read = false;
|
|
3663
|
+
// ../packages/cli/src/commands/new/templates/xaendar-json.ts
|
|
3664
|
+
function xaendarJson(name, style) {
|
|
3665
|
+
return `{
|
|
3666
|
+
"name": "${name}",
|
|
3667
|
+
"entry": "src/main.ts",
|
|
3668
|
+
"outDir": "dist",
|
|
3669
|
+
"assetsDir": "assets",
|
|
3670
|
+
"generate": {
|
|
3671
|
+
"components": {
|
|
3672
|
+
"style": "${style}"
|
|
4128
3673
|
}
|
|
4129
3674
|
}
|
|
4130
|
-
return retVal;
|
|
4131
3675
|
}
|
|
4132
|
-
|
|
4133
|
-
// ../packages/compiler/src/lexer/states/tag-close.state.ts
|
|
4134
|
-
function consumeTagClose(cursor, _context) {
|
|
4135
|
-
let read = true;
|
|
4136
|
-
let tagName = "";
|
|
4137
|
-
let retVal;
|
|
4138
|
-
cursor.advance(2);
|
|
4139
|
-
cursor.skipSpaces();
|
|
4140
|
-
while (read) {
|
|
4141
|
-
switch (cursor.peek()) {
|
|
4142
|
-
case GREATER_THEN:
|
|
4143
|
-
cursor.advance();
|
|
4144
|
-
retVal = {
|
|
4145
|
-
state: "text" /* TEXT */,
|
|
4146
|
-
tokens: [{
|
|
4147
|
-
type: 4 /* TAG_CLOSE_NAME */,
|
|
4148
|
-
parts: [tagName]
|
|
4149
|
-
}]
|
|
4150
|
-
};
|
|
4151
|
-
read = false;
|
|
4152
|
-
break;
|
|
4153
|
-
case SPACE:
|
|
4154
|
-
throw new Error("Tag Close Name cannot contains spaces");
|
|
4155
|
-
default:
|
|
4156
|
-
cursor.advance();
|
|
4157
|
-
tagName = `${tagName}${cursor.currentChar.value}`;
|
|
4158
|
-
}
|
|
4159
|
-
}
|
|
4160
|
-
return retVal;
|
|
3676
|
+
`;
|
|
4161
3677
|
}
|
|
4162
3678
|
|
|
4163
|
-
// ../packages/
|
|
4164
|
-
function
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
state: "text" /* TEXT */,
|
|
4170
|
-
tokens: [{
|
|
4171
|
-
type: 3 /* TAG_OPEN_END */,
|
|
4172
|
-
parts: []
|
|
4173
|
-
}]
|
|
4174
|
-
};
|
|
4175
|
-
} else {
|
|
4176
|
-
cursor.advance();
|
|
4177
|
-
const nextChar = cursor.peek();
|
|
4178
|
-
if (nextChar === GREATER_THEN) {
|
|
4179
|
-
cursor.advance();
|
|
4180
|
-
retVal = {
|
|
4181
|
-
state: "text" /* TEXT */,
|
|
4182
|
-
tokens: [{
|
|
4183
|
-
type: 2 /* TAG_SELF_CLOSE */,
|
|
4184
|
-
parts: []
|
|
4185
|
-
}]
|
|
4186
|
-
};
|
|
4187
|
-
} else {
|
|
4188
|
-
throw new Error(`Unexpected character ${nextChar} for closing tag.
|
|
4189
|
-
Expected />
|
|
4190
|
-
Read of /${String.fromCharCode(nextChar)}
|
|
4191
|
-
At line ${cursor.position.row + 1} col ${cursor.position.column + 1}`);
|
|
3679
|
+
// ../packages/cli/src/commands/new/structure.ts
|
|
3680
|
+
function readCliVersion() {
|
|
3681
|
+
try {
|
|
3682
|
+
const cliPackageJson = JSON.parse(readFileSync("./package.json", "utf8"));
|
|
3683
|
+
if (!cliPackageJson.version) {
|
|
3684
|
+
throw new Error("Unable to determine Xaendar CLI version.");
|
|
4192
3685
|
}
|
|
3686
|
+
return cliPackageJson.version;
|
|
3687
|
+
} catch (error) {
|
|
3688
|
+
console.error("Error reading Xaendar CLI version:", error);
|
|
3689
|
+
process.exit(1);
|
|
4193
3690
|
}
|
|
4194
|
-
return retVal;
|
|
4195
3691
|
}
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
3692
|
+
function buildStructure(context) {
|
|
3693
|
+
const version = readCliVersion();
|
|
3694
|
+
const componentName = context.name;
|
|
3695
|
+
return [
|
|
3696
|
+
{
|
|
3697
|
+
type: "file",
|
|
3698
|
+
name: "package.json",
|
|
3699
|
+
content: packageJson(componentName, version)
|
|
3700
|
+
},
|
|
3701
|
+
{
|
|
3702
|
+
type: "file",
|
|
3703
|
+
name: "xaendar.json",
|
|
3704
|
+
content: xaendarJson(componentName, context.style)
|
|
3705
|
+
},
|
|
3706
|
+
{
|
|
3707
|
+
type: "file",
|
|
3708
|
+
name: "vite.config.ts",
|
|
3709
|
+
content: viteConfigTs()
|
|
3710
|
+
},
|
|
3711
|
+
{
|
|
3712
|
+
type: "file",
|
|
3713
|
+
name: "tsconfig.json",
|
|
3714
|
+
content: tsconfigJson()
|
|
3715
|
+
},
|
|
3716
|
+
{
|
|
3717
|
+
type: "directory",
|
|
3718
|
+
name: "src",
|
|
3719
|
+
children: [
|
|
3720
|
+
{
|
|
3721
|
+
type: "file",
|
|
3722
|
+
name: "index.html",
|
|
3723
|
+
content: indexHtml(componentName)
|
|
3724
|
+
},
|
|
3725
|
+
{
|
|
3726
|
+
type: "file",
|
|
3727
|
+
name: "main.ts",
|
|
3728
|
+
content: mainTs(componentName)
|
|
3729
|
+
},
|
|
3730
|
+
{
|
|
3731
|
+
type: "generateComponent",
|
|
3732
|
+
name: componentName
|
|
3733
|
+
}
|
|
3734
|
+
]
|
|
4221
3735
|
}
|
|
4222
|
-
|
|
4223
|
-
return retVal;
|
|
3736
|
+
];
|
|
4224
3737
|
}
|
|
4225
3738
|
|
|
4226
|
-
// ../packages/
|
|
4227
|
-
function
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
retVal = { state: nextState };
|
|
4236
|
-
read = false;
|
|
4237
|
-
break;
|
|
4238
|
-
case LEFT_BRACE:
|
|
4239
|
-
retVal = {
|
|
4240
|
-
state: "interpolation" /* INTERPOLATION */,
|
|
4241
|
-
pushState: true
|
|
4242
|
-
};
|
|
4243
|
-
read = false;
|
|
4244
|
-
break;
|
|
4245
|
-
case AT_SIGN:
|
|
4246
|
-
retVal = {
|
|
4247
|
-
state: "flow-control" /* FLOW_CONTROL */
|
|
4248
|
-
};
|
|
4249
|
-
read = false;
|
|
4250
|
-
break;
|
|
4251
|
-
case RIGHT_BRACE:
|
|
4252
|
-
if (context.history[context.history.length - 1] === "flow-control-block" /* FLOW_CONTROL_BLOCK */) {
|
|
4253
|
-
cursor.advance();
|
|
4254
|
-
retVal = {
|
|
4255
|
-
state: "text" /* TEXT */,
|
|
4256
|
-
tokens: [{ type: 17 /* BLOCK_CLOSE */ }],
|
|
4257
|
-
popState: true
|
|
4258
|
-
};
|
|
4259
|
-
read = false;
|
|
4260
|
-
} else {
|
|
4261
|
-
cursor.advance();
|
|
4262
|
-
text = `${text}${cursor.currentChar.value}`;
|
|
4263
|
-
}
|
|
4264
|
-
break;
|
|
4265
|
-
case LF:
|
|
4266
|
-
case CR:
|
|
4267
|
-
cursor.advance();
|
|
4268
|
-
break;
|
|
4269
|
-
default:
|
|
4270
|
-
cursor.advance();
|
|
4271
|
-
text = `${text}${cursor.currentChar.value}`;
|
|
4272
|
-
}
|
|
4273
|
-
}
|
|
4274
|
-
retVal.tokens ??= isNotBlank(text) ? [{ type: 0 /* TEXT */, parts: [text] }] : void 0;
|
|
4275
|
-
return retVal;
|
|
3739
|
+
// ../packages/cli/src/commands/new/new.command.ts
|
|
3740
|
+
function newCommand() {
|
|
3741
|
+
return new Command("new").description("Sccafold a new Xaendar project").argument("<name>", "Name of the project to create").option("-p, --path <path>", "Custom path for the generated project (default: current directory)").option("-s, --style <style>", "CSS preprocessor to use (css, scss, less, styl)", "css").action((name, options) => {
|
|
3742
|
+
const style = resolveStyleOptions(options.style);
|
|
3743
|
+
const workingDirectory = options.path || process.cwd();
|
|
3744
|
+
const projectDirectory = resolve(workingDirectory, name);
|
|
3745
|
+
checkAndCreateProjectDirectory(projectDirectory);
|
|
3746
|
+
createFiles(buildStructure({ name, style }), projectDirectory, style);
|
|
3747
|
+
});
|
|
4276
3748
|
}
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
* Creates a new Cursor instance for the given template content.
|
|
4282
|
-
*
|
|
4283
|
-
* @param input The full template text that the cursor will navigate.
|
|
4284
|
-
*/
|
|
4285
|
-
constructor(input) {
|
|
4286
|
-
this.input = input;
|
|
4287
|
-
this._cursor = new LexerCursor(this.input);
|
|
4288
|
-
}
|
|
4289
|
-
input;
|
|
4290
|
-
_cursor;
|
|
4291
|
-
_state = "start" /* START */;
|
|
4292
|
-
_stack = new Stack();
|
|
4293
|
-
_tokens = new Array();
|
|
4294
|
-
_states = {
|
|
4295
|
-
["start" /* START */]: consumeText,
|
|
4296
|
-
["text" /* TEXT */]: consumeText,
|
|
4297
|
-
["tag-open-name" /* TAG_OPEN_NAME */]: consumeTagOpenName,
|
|
4298
|
-
["tag-body" /* TAG_BODY */]: consumeTagBody,
|
|
4299
|
-
["tag-open-end" /* TAG_OPEN_END */]: consumeTagOpenEnd,
|
|
4300
|
-
["tag-close" /* TAG_CLOSE */]: consumeTagClose,
|
|
4301
|
-
["attribute" /* ATTRIBUTE */]: consumeAttribute,
|
|
4302
|
-
["flow-control" /* FLOW_CONTROL */]: consumeFlowControl,
|
|
4303
|
-
["flow-control-condition" /* FLOW_CONTROL_CONDITION */]: consumeFlowControlCondition,
|
|
4304
|
-
["flow-control-block" /* FLOW_CONTROL_BLOCK */]: consumeFlowControlBlock,
|
|
4305
|
-
["event" /* EVENT */]: consumeEvent,
|
|
4306
|
-
["interpolation" /* INTERPOLATION */]: consumeInterpolation,
|
|
4307
|
-
["interpolation-expression" /* INTERPOLATION_EXPRESSION */]: consumeInterpolationExpression,
|
|
4308
|
-
["interpolation-literal" /* INTERPOLATION_LITERAL */]: consumeInterpolationliteral
|
|
4309
|
-
};
|
|
4310
|
-
tokenize() {
|
|
4311
|
-
let eof = false;
|
|
4312
|
-
while (!eof) {
|
|
4313
|
-
try {
|
|
4314
|
-
const transitionFunction = this._states[this._state];
|
|
4315
|
-
const { state, tokens, popState, pushState } = transitionFunction(this._cursor, { history: this._stack.values });
|
|
4316
|
-
if (tokens?.length) {
|
|
4317
|
-
this._tokens.push(...tokens);
|
|
4318
|
-
}
|
|
4319
|
-
if (pushState) {
|
|
4320
|
-
this._stack.push(this._state);
|
|
4321
|
-
}
|
|
4322
|
-
if (popState) {
|
|
4323
|
-
this._stack.pop();
|
|
4324
|
-
}
|
|
4325
|
-
this._state = state;
|
|
4326
|
-
} catch (err) {
|
|
4327
|
-
const error = err;
|
|
4328
|
-
if (error.cause === EOF) {
|
|
4329
|
-
eof = true;
|
|
4330
|
-
} else {
|
|
4331
|
-
throw err;
|
|
4332
|
-
}
|
|
4333
|
-
}
|
|
4334
|
-
}
|
|
4335
|
-
return this._tokens;
|
|
4336
|
-
}
|
|
4337
|
-
};
|
|
4338
|
-
|
|
4339
|
-
// ../packages/compiler/src/parser/models/parser-cursor.model.ts
|
|
4340
|
-
var ParserCursor = class {
|
|
4341
|
-
/**
|
|
4342
|
-
* Creates a new ParserCursor for the given token array.
|
|
4343
|
-
*
|
|
4344
|
-
* @param _tokens Array of tokens to navigate.
|
|
4345
|
-
*/
|
|
4346
|
-
constructor(_tokens) {
|
|
4347
|
-
this._tokens = _tokens;
|
|
4348
|
-
}
|
|
4349
|
-
_tokens;
|
|
4350
|
-
/**
|
|
4351
|
-
* Representation of the current token.
|
|
4352
|
-
*
|
|
4353
|
-
* - `index`: absolute index within the token array
|
|
4354
|
-
* - `value`: current token object (or EOF token)
|
|
4355
|
-
*
|
|
4356
|
-
* An index of `-1` indicates that the cursor has not
|
|
4357
|
-
* yet consumed any token or has reached EOF.
|
|
4358
|
-
*/
|
|
4359
|
-
_currentToken = {
|
|
4360
|
-
value: { type: 18 /* EOF */ },
|
|
4361
|
-
index: -1
|
|
4362
|
-
};
|
|
4363
|
-
/**
|
|
4364
|
-
* Returns a read-only snapshot of the current token.
|
|
4365
|
-
*/
|
|
4366
|
-
get currentToken() {
|
|
4367
|
-
return this._currentToken;
|
|
4368
|
-
}
|
|
4369
|
-
/**
|
|
4370
|
-
* Advances the cursor by the specified number of tokens.
|
|
4371
|
-
*
|
|
4372
|
-
* Updates the current token and index.
|
|
4373
|
-
*
|
|
4374
|
-
* @param chars Number of tokens to advance (must be >= 1)
|
|
4375
|
-
*
|
|
4376
|
-
* @throws Error with cause `EOF` when advancing past the end
|
|
4377
|
-
*/
|
|
4378
|
-
advance(chars = 1) {
|
|
4379
|
-
if (chars < 1) {
|
|
4380
|
-
throw new Error(`${chars} is not a valid value. Please enter a number equal or greater than 1`);
|
|
4381
|
-
}
|
|
4382
|
-
const newIndex = this._currentToken.index + chars;
|
|
4383
|
-
if (newIndex >= this._tokens.length) {
|
|
4384
|
-
this._currentToken.value = { type: 18 /* EOF */ };
|
|
4385
|
-
this._currentToken.index = -1;
|
|
4386
|
-
this.throwEOFError();
|
|
4387
|
-
} else {
|
|
4388
|
-
this._currentToken.index = newIndex;
|
|
4389
|
-
this._currentToken.value = this._tokens[newIndex];
|
|
4390
|
-
}
|
|
4391
|
-
}
|
|
4392
|
-
peek(charsOrOptions, options) {
|
|
4393
|
-
const tokens = typeof charsOrOptions === "number" ? charsOrOptions : 1;
|
|
4394
|
-
const offset = (typeof charsOrOptions === "object" ? charsOrOptions : options)?.offset ?? 0;
|
|
4395
|
-
return tokens === 1 ? this.peekOneToken(this._currentToken.index + offset + 1) : this.peekMany(tokens + offset);
|
|
4396
|
-
}
|
|
4397
|
-
/**
|
|
4398
|
-
* Peeks multiple tokens ahead.
|
|
4399
|
-
*/
|
|
4400
|
-
peekMany(chars) {
|
|
4401
|
-
const peekedTokens = [];
|
|
4402
|
-
const nextTokenIndex = this._currentToken.index + 1;
|
|
4403
|
-
for (let i = nextTokenIndex; i < nextTokenIndex + chars; i++) {
|
|
4404
|
-
peekedTokens.push(this.peekOneToken(i));
|
|
4405
|
-
}
|
|
4406
|
-
return peekedTokens;
|
|
3749
|
+
function resolveStyleOptions(style) {
|
|
3750
|
+
const validStyles = ["css", "scss", "less", "styl"];
|
|
3751
|
+
if (!style) {
|
|
3752
|
+
return "css";
|
|
4407
3753
|
}
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
peekOneToken(index) {
|
|
4412
|
-
if (index >= this._tokens.length) {
|
|
4413
|
-
this.throwEOFError();
|
|
4414
|
-
}
|
|
4415
|
-
return this._tokens[index];
|
|
4416
|
-
}
|
|
4417
|
-
/**
|
|
4418
|
-
* Throws a standardized EOF error used by the parser
|
|
4419
|
-
* to terminate token consumption.
|
|
4420
|
-
*/
|
|
4421
|
-
throwEOFError() {
|
|
4422
|
-
throw new Error("", { cause: EOF });
|
|
4423
|
-
}
|
|
4424
|
-
};
|
|
4425
|
-
|
|
4426
|
-
// ../packages/compiler/src/parser/parser.ts
|
|
4427
|
-
var Parser = class {
|
|
4428
|
-
/**
|
|
4429
|
-
* Creates a new Parser instance.
|
|
4430
|
-
*
|
|
4431
|
-
* @param tokens Array of tokens produced by the Lexer
|
|
4432
|
-
*/
|
|
4433
|
-
constructor(tokens) {
|
|
4434
|
-
this.tokens = tokens;
|
|
4435
|
-
this._cursor = new ParserCursor(this.tokens);
|
|
4436
|
-
}
|
|
4437
|
-
tokens;
|
|
4438
|
-
/** Internal cursor for navigating tokens */
|
|
4439
|
-
_cursor;
|
|
4440
|
-
/**
|
|
4441
|
-
* Entry point for parsing the token stream into AST nodes.
|
|
4442
|
-
*
|
|
4443
|
-
* @returns Array of top-level AST nodes
|
|
4444
|
-
*/
|
|
4445
|
-
parse() {
|
|
4446
|
-
let eof = false;
|
|
4447
|
-
const nodes = [];
|
|
4448
|
-
while (!eof) {
|
|
4449
|
-
try {
|
|
4450
|
-
nodes.push(this.parseNode());
|
|
4451
|
-
} catch (err) {
|
|
4452
|
-
const error = err;
|
|
4453
|
-
if (error.cause === EOF) {
|
|
4454
|
-
eof = true;
|
|
4455
|
-
} else {
|
|
4456
|
-
throw err;
|
|
4457
|
-
}
|
|
4458
|
-
}
|
|
4459
|
-
}
|
|
4460
|
-
return nodes;
|
|
4461
|
-
}
|
|
4462
|
-
/**
|
|
4463
|
-
* Parses the next AST node based on the current token.
|
|
4464
|
-
*
|
|
4465
|
-
* @returns Parsed AST node
|
|
4466
|
-
* @throws Error if an unexpected token is encountered
|
|
4467
|
-
*/
|
|
4468
|
-
parseNode() {
|
|
4469
|
-
const token = this._cursor.peek();
|
|
4470
|
-
switch (token.type) {
|
|
4471
|
-
case 0 /* TEXT */:
|
|
4472
|
-
return this.parseText(token);
|
|
4473
|
-
case 8 /* INTERPOLATION_EXPRESSION */:
|
|
4474
|
-
case 7 /* INTERPOLATION_LITERAL */:
|
|
4475
|
-
return this.parseInterpolation(token);
|
|
4476
|
-
case 1 /* TAG_OPEN_NAME */:
|
|
4477
|
-
return this.parseElement(token);
|
|
4478
|
-
case 9 /* IF */:
|
|
4479
|
-
return this.parseIfControlFlow(token);
|
|
4480
|
-
case 10 /* FOR */:
|
|
4481
|
-
return this.parseForControlFlow(token);
|
|
4482
|
-
case 12 /* SWITCH */:
|
|
4483
|
-
return this.parseSwitchControlFlow(token);
|
|
4484
|
-
default:
|
|
4485
|
-
throw this.error(`Unexpected token ${TokenType[token.type]}`);
|
|
4486
|
-
}
|
|
3754
|
+
if (!validStyles.includes(style)) {
|
|
3755
|
+
console.error(`\u2716 Invalid style option: ${style}`);
|
|
3756
|
+
process.exit(1);
|
|
4487
3757
|
}
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
parseIfControlFlow(_token) {
|
|
4496
|
-
this._cursor.advance();
|
|
4497
|
-
const conditionToken = this._cursor.peek();
|
|
4498
|
-
if (conditionToken.type !== 15 /* CONDITION */) {
|
|
4499
|
-
throw this.error(`Expected CONDITION after IF, got ${TokenType[conditionToken.type]}`);
|
|
4500
|
-
}
|
|
4501
|
-
const condition = conditionToken.parts[0];
|
|
4502
|
-
this._cursor.advance();
|
|
4503
|
-
this._cursor.advance();
|
|
4504
|
-
const consequent = this.parseBlockChildren();
|
|
4505
|
-
let alternate = null;
|
|
4506
|
-
const next = this._cursor.peek();
|
|
4507
|
-
if (next.type === 11 /* ELSE */) {
|
|
4508
|
-
this._cursor.advance();
|
|
4509
|
-
this._cursor.advance();
|
|
4510
|
-
const elseChildren = this.parseBlockChildren();
|
|
4511
|
-
alternate = { type: 4 /* Else */, children: elseChildren };
|
|
4512
|
-
}
|
|
4513
|
-
return { type: 3 /* If */, condition, consequent, alternate };
|
|
3758
|
+
return style;
|
|
3759
|
+
}
|
|
3760
|
+
function checkAndCreateProjectDirectory(path) {
|
|
3761
|
+
const exists = existsSync2(path);
|
|
3762
|
+
if (exists && readdirSync(path).length) {
|
|
3763
|
+
console.error(`\u2716 Cannot create project: target directory is not empty: ${path}`);
|
|
3764
|
+
process.exit(1);
|
|
4514
3765
|
}
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
*
|
|
4518
|
-
* Expected token sequence:
|
|
4519
|
-
* FOR → CONDITION → BLOCK_OPEN → ...children... → BLOCK_CLOSE
|
|
4520
|
-
*/
|
|
4521
|
-
parseForControlFlow(_token) {
|
|
4522
|
-
this._cursor.advance();
|
|
4523
|
-
const conditionToken = this._cursor.peek();
|
|
4524
|
-
if (conditionToken.type !== 15 /* CONDITION */) {
|
|
4525
|
-
throw this.error(`Expected CONDITION after FOR, got ${TokenType[conditionToken.type]}`);
|
|
4526
|
-
}
|
|
4527
|
-
const expression = conditionToken.parts[0];
|
|
4528
|
-
this._cursor.advance();
|
|
4529
|
-
this._cursor.advance();
|
|
4530
|
-
const children = this.parseBlockChildren();
|
|
4531
|
-
return { type: 5 /* For */, expression, children };
|
|
3766
|
+
if (!exists) {
|
|
3767
|
+
mkdirSync2(path);
|
|
4532
3768
|
}
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
}
|
|
4548
|
-
const expression = conditionToken.parts[0];
|
|
4549
|
-
this._cursor.advance();
|
|
4550
|
-
this._cursor.advance();
|
|
4551
|
-
const cases = [];
|
|
4552
|
-
while (this._cursor.peek().type !== 17 /* BLOCK_CLOSE */) {
|
|
4553
|
-
const t = this._cursor.peek();
|
|
4554
|
-
if (t.type === 13 /* CASE */) {
|
|
4555
|
-
this._cursor.advance();
|
|
4556
|
-
const caseCondition = this._cursor.peek();
|
|
4557
|
-
if (caseCondition.type !== 15 /* CONDITION */) {
|
|
4558
|
-
throw this.error(`Expected CONDITION after CASE`);
|
|
3769
|
+
}
|
|
3770
|
+
function createFiles(entries, basePath, style) {
|
|
3771
|
+
entries.forEach((entry) => {
|
|
3772
|
+
switch (entry.type) {
|
|
3773
|
+
case "file":
|
|
3774
|
+
case void 0:
|
|
3775
|
+
const content = entry.content;
|
|
3776
|
+
writeFileSync2(resolve(basePath, entry.name), content, "utf8");
|
|
3777
|
+
break;
|
|
3778
|
+
case "directory":
|
|
3779
|
+
const dirPath = resolve(basePath, entry.name);
|
|
3780
|
+
mkdirSync2(dirPath);
|
|
3781
|
+
if (entry.children) {
|
|
3782
|
+
createFiles(entry.children, dirPath, style);
|
|
4559
3783
|
}
|
|
4560
|
-
const caseExpr = caseCondition.parts[0];
|
|
4561
|
-
this._cursor.advance();
|
|
4562
|
-
this._cursor.advance();
|
|
4563
|
-
cases.push({ type: 7 /* Case */, condition: caseExpr, children: this.parseBlockChildren() });
|
|
4564
|
-
} else if (t.type === 14 /* DEFAULT */) {
|
|
4565
|
-
this._cursor.advance();
|
|
4566
|
-
this._cursor.advance();
|
|
4567
|
-
cases.push({ type: 7 /* Case */, condition: null, children: this.parseBlockChildren() });
|
|
4568
|
-
} else {
|
|
4569
3784
|
break;
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
return { type: 6 /* Switch */, expression, cases };
|
|
4574
|
-
}
|
|
4575
|
-
/**
|
|
4576
|
-
* Parses child nodes until a BLOCK_CLOSE token is encountered.
|
|
4577
|
-
* Consumes the BLOCK_CLOSE before returning.
|
|
4578
|
-
*/
|
|
4579
|
-
parseBlockChildren() {
|
|
4580
|
-
const children = [];
|
|
4581
|
-
while (this._cursor.peek().type !== 17 /* BLOCK_CLOSE */) {
|
|
4582
|
-
children.push(this.parseNode());
|
|
4583
|
-
}
|
|
4584
|
-
this._cursor.advance();
|
|
4585
|
-
return children;
|
|
4586
|
-
}
|
|
4587
|
-
/**
|
|
4588
|
-
* Parses a text token into a TextNode.
|
|
4589
|
-
*/
|
|
4590
|
-
parseText(token) {
|
|
4591
|
-
this._cursor.advance();
|
|
4592
|
-
return {
|
|
4593
|
-
type: 1 /* Text */,
|
|
4594
|
-
value: token.parts[0]
|
|
4595
|
-
};
|
|
4596
|
-
}
|
|
4597
|
-
/**
|
|
4598
|
-
* Parses an interpolation token into an InterpolationNode.
|
|
4599
|
-
*/
|
|
4600
|
-
parseInterpolation(token) {
|
|
4601
|
-
this._cursor.advance();
|
|
4602
|
-
return {
|
|
4603
|
-
type: 2 /* Interpolation */,
|
|
4604
|
-
expression: token.parts[0]
|
|
4605
|
-
};
|
|
4606
|
-
}
|
|
4607
|
-
/**
|
|
4608
|
-
* Parses an element starting from a TAG_OPEN_NAME token.
|
|
4609
|
-
* Handles attributes, events, children, and tag closure.
|
|
4610
|
-
*/
|
|
4611
|
-
parseElement(token) {
|
|
4612
|
-
this._cursor.advance();
|
|
4613
|
-
const tagName = token.parts[0];
|
|
4614
|
-
const attributes = new Array();
|
|
4615
|
-
const events = new Array();
|
|
4616
|
-
let read = true;
|
|
4617
|
-
while (read) {
|
|
4618
|
-
const token2 = this._cursor.peek();
|
|
4619
|
-
switch (token2.type) {
|
|
4620
|
-
case 5 /* ATTRIBUTE */:
|
|
4621
|
-
attributes.push(this.parseAttribute(token2));
|
|
4622
|
-
break;
|
|
4623
|
-
case 6 /* EVENT */:
|
|
4624
|
-
events.push(this.parseEvent(token2));
|
|
4625
|
-
break;
|
|
4626
|
-
default:
|
|
4627
|
-
read = false;
|
|
4628
|
-
}
|
|
4629
|
-
}
|
|
4630
|
-
if (this._cursor.peek().type === 3 /* TAG_OPEN_END */) {
|
|
4631
|
-
this._cursor.advance();
|
|
4632
|
-
}
|
|
4633
|
-
if (this._cursor.peek().type === 2 /* TAG_SELF_CLOSE */) {
|
|
4634
|
-
this._cursor.advance();
|
|
4635
|
-
return {
|
|
4636
|
-
type: 0 /* Element */,
|
|
4637
|
-
tagName,
|
|
4638
|
-
attributes,
|
|
4639
|
-
events,
|
|
4640
|
-
children: []
|
|
4641
|
-
};
|
|
4642
|
-
}
|
|
4643
|
-
const children = [];
|
|
4644
|
-
while (!this.isTagClose(tagName)) {
|
|
4645
|
-
children.push(this.parseNode());
|
|
4646
|
-
}
|
|
4647
|
-
this._cursor.advance();
|
|
4648
|
-
return {
|
|
4649
|
-
type: 0 /* Element */,
|
|
4650
|
-
tagName,
|
|
4651
|
-
attributes,
|
|
4652
|
-
events,
|
|
4653
|
-
children
|
|
4654
|
-
};
|
|
4655
|
-
}
|
|
4656
|
-
/**
|
|
4657
|
-
* Parses an attribute token into an AttributeNode.
|
|
4658
|
-
* Supports literal values and interpolations as attribute values.
|
|
4659
|
-
*/
|
|
4660
|
-
parseAttribute(token) {
|
|
4661
|
-
this._cursor.advance();
|
|
4662
|
-
const raw = token.parts[0];
|
|
4663
|
-
if (!raw.includes("=")) {
|
|
4664
|
-
return { name: raw, value: "true" };
|
|
4665
|
-
}
|
|
4666
|
-
const [name, value] = raw.split("=");
|
|
4667
|
-
if (!name || !value) {
|
|
4668
|
-
throw this.error(`Invalid attribute format: ${raw}`);
|
|
4669
|
-
}
|
|
4670
|
-
const nextToken = this._cursor.peek();
|
|
4671
|
-
if (nextToken.type === 8 /* INTERPOLATION_EXPRESSION */ || nextToken.type === 7 /* INTERPOLATION_LITERAL */) {
|
|
4672
|
-
return {
|
|
4673
|
-
name,
|
|
4674
|
-
value: this.parseInterpolation(nextToken)
|
|
4675
|
-
};
|
|
4676
|
-
}
|
|
4677
|
-
return {
|
|
4678
|
-
name,
|
|
4679
|
-
value: value.replace(/^['']|['']$/g, "")
|
|
4680
|
-
};
|
|
4681
|
-
}
|
|
4682
|
-
/**
|
|
4683
|
-
* Parses an event token into an EventNode.
|
|
4684
|
-
*/
|
|
4685
|
-
parseEvent(token) {
|
|
4686
|
-
this._cursor.advance();
|
|
4687
|
-
const raw = token.parts[0];
|
|
4688
|
-
const [name, value] = raw.split("=");
|
|
4689
|
-
if (!name || !value) {
|
|
4690
|
-
throw this.error(`Invalid event format: ${raw}`);
|
|
3785
|
+
case "generateComponent":
|
|
3786
|
+
generateComponent(entry.name, basePath, false, style);
|
|
3787
|
+
break;
|
|
4691
3788
|
}
|
|
4692
|
-
return {
|
|
4693
|
-
name,
|
|
4694
|
-
handler: value.replace(/^[""]|[""]$/g, "")
|
|
4695
|
-
};
|
|
4696
|
-
}
|
|
4697
|
-
/**
|
|
4698
|
-
* Checks whether the next token is a closing tag matching the given name.
|
|
4699
|
-
*/
|
|
4700
|
-
isTagClose(tagName) {
|
|
4701
|
-
const nextToken = this._cursor.peek();
|
|
4702
|
-
return nextToken.type === 4 /* TAG_CLOSE_NAME */ && nextToken.parts[0] === tagName;
|
|
4703
|
-
}
|
|
4704
|
-
/**
|
|
4705
|
-
* Creates a parser error with a consistent prefix.
|
|
4706
|
-
*/
|
|
4707
|
-
error(message) {
|
|
4708
|
-
return new Error(`[Parser] ${message}`);
|
|
4709
|
-
}
|
|
4710
|
-
};
|
|
4711
|
-
|
|
4712
|
-
// ../packages/compiler/src/render-generator/render-generator.model.ts
|
|
4713
|
-
function generateRenderFunction(ast, componentVar = "this") {
|
|
4714
|
-
return [
|
|
4715
|
-
`
|
|
4716
|
-
const shadow = ${componentVar}.shadowRoot!;
|
|
4717
|
-
`,
|
|
4718
|
-
...ast.map((node, i) => processNode(node, `node${i}`, componentVar, "shadow")).flat()
|
|
4719
|
-
].join("\n");
|
|
4720
|
-
}
|
|
4721
|
-
function processNode(node, varName, componentVar, parentVar) {
|
|
4722
|
-
switch (node.type) {
|
|
4723
|
-
case 1 /* Text */:
|
|
4724
|
-
case 2 /* Interpolation */:
|
|
4725
|
-
return processTextAndInterpolation(node, varName, componentVar, parentVar);
|
|
4726
|
-
case 0 /* Element */:
|
|
4727
|
-
return processElement(node, varName, componentVar, parentVar);
|
|
4728
|
-
case 3 /* If */:
|
|
4729
|
-
return processIf(node, varName, componentVar, parentVar);
|
|
4730
|
-
case 5 /* For */:
|
|
4731
|
-
return processFor(node, varName, componentVar, parentVar);
|
|
4732
|
-
case 6 /* Switch */:
|
|
4733
|
-
return processSwitch(node, varName, componentVar, parentVar);
|
|
4734
|
-
default:
|
|
4735
|
-
return [];
|
|
4736
|
-
}
|
|
4737
|
-
}
|
|
4738
|
-
function processTextAndInterpolation(node, varName, componentVar, parentVar) {
|
|
4739
|
-
const textValue = node.type === 1 /* Text */ ? JSON.stringify(node.value) : resolveExpression(node.expression, componentVar);
|
|
4740
|
-
return [
|
|
4741
|
-
`const ${varName} = document.createTextNode(${textValue});`,
|
|
4742
|
-
`${parentVar}.appendChild(${varName});`
|
|
4743
|
-
];
|
|
4744
|
-
}
|
|
4745
|
-
function processElement(node, varName, componentVar, parentVar) {
|
|
4746
|
-
return [
|
|
4747
|
-
`const ${varName} = document.createElement("${node.tagName}");`,
|
|
4748
|
-
...node.attributes?.map((attr) => `${varName}.setAttribute('${attr.name}', ${typeof attr.value === "string" ? attr.value : `${componentVar}.${attr.value.expression}`});`) || [],
|
|
4749
|
-
...node.events?.map((event) => `${varName}.addEventListener("${event.name}", ${componentVar}.${event.handler}.bind(${componentVar}));`) || [],
|
|
4750
|
-
`${parentVar}.appendChild(${varName});`,
|
|
4751
|
-
...node.children.map((child, i) => processNode(child, `${varName}_c${i}`, componentVar, varName)).flat()
|
|
4752
|
-
];
|
|
4753
|
-
}
|
|
4754
|
-
function processIf(node, varName, componentVar, parentVar) {
|
|
4755
|
-
const code = [
|
|
4756
|
-
`if (${resolveExpression(node.condition, componentVar)}) {`,
|
|
4757
|
-
...node.consequent.map((child, idx) => indent(...processNode(child, `${varName}_t${idx}`, componentVar, parentVar))).flat(),
|
|
4758
|
-
"}"
|
|
4759
|
-
];
|
|
4760
|
-
const alt = node.alternate;
|
|
4761
|
-
if (alt) {
|
|
4762
|
-
code[code.length - 1] += " else {";
|
|
4763
|
-
code.push(
|
|
4764
|
-
...alt.children.map((child, idx) => indent(...processNode(child, `${varName}_e${idx}`, componentVar, parentVar))).flat(),
|
|
4765
|
-
"}"
|
|
4766
|
-
);
|
|
4767
|
-
}
|
|
4768
|
-
return code;
|
|
4769
|
-
}
|
|
4770
|
-
function processFor(node, varName, componentVar, parentVar) {
|
|
4771
|
-
const iterExpr = resolveForExpression(node.expression, componentVar);
|
|
4772
|
-
return [
|
|
4773
|
-
`for (${iterExpr}) {`,
|
|
4774
|
-
...node.children.map((child, idx) => indent(...processNode(child, `${varName}_f${idx}`, componentVar, parentVar))).flat(),
|
|
4775
|
-
"}"
|
|
4776
|
-
];
|
|
4777
|
-
}
|
|
4778
|
-
function processSwitch(node, varName, componentVar, parentVar) {
|
|
4779
|
-
return [
|
|
4780
|
-
`switch (${resolveExpression(node.expression, componentVar)}) {`,
|
|
4781
|
-
...node.cases.map((caseNode) => [
|
|
4782
|
-
...indent(
|
|
4783
|
-
`${!caseNode.condition ? "default" : `case ${caseNode.condition}`}: {`,
|
|
4784
|
-
...caseNode.children.map((child, i) => indent(...processNode(child, `${varName}_s${i}_${i}`, componentVar, parentVar))).flat(),
|
|
4785
|
-
`${indent("break;")}`,
|
|
4786
|
-
`}`
|
|
4787
|
-
)
|
|
4788
|
-
]).flat(),
|
|
4789
|
-
"}"
|
|
4790
|
-
];
|
|
4791
|
-
}
|
|
4792
|
-
function resolveForExpression(expression, componentVar) {
|
|
4793
|
-
const match = expression.match(/^let\s+(\w+)\s+of\s+(\w+)$/);
|
|
4794
|
-
if (!match) {
|
|
4795
|
-
throw new Error(`String "${expression}" does not match the structure "let X of Y"`);
|
|
4796
|
-
}
|
|
4797
|
-
const [, X, Y] = match;
|
|
4798
|
-
return `const ${X} of this.${Y}`;
|
|
4799
|
-
}
|
|
4800
|
-
function resolveExpression(expression, componentVar) {
|
|
4801
|
-
return expression.replace(/\b([a-zA-Z_$][a-zA-Z0-9_$]*)\b/g, (match) => match === componentVar ? match : `${componentVar}.${match}`);
|
|
4802
|
-
}
|
|
4803
|
-
function indent(...lines) {
|
|
4804
|
-
return lines.map((line) => ` ${line}`);
|
|
4805
|
-
}
|
|
4806
|
-
|
|
4807
|
-
// ../packages/cli/src/commands/compile/compile.command.ts
|
|
4808
|
-
import { readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
4809
|
-
import { basename, dirname, extname, resolve } from "path";
|
|
4810
|
-
function compileFile(inputPath, outputPath) {
|
|
4811
|
-
const absInput = resolve(process.cwd(), inputPath);
|
|
4812
|
-
const absOutput = outputPath ? resolve(process.cwd(), outputPath) : resolve(dirname(absInput), `${basename(absInput, extname(absInput))}.render.js`);
|
|
4813
|
-
let source;
|
|
4814
|
-
try {
|
|
4815
|
-
source = readFileSync(absInput, "utf8");
|
|
4816
|
-
} catch {
|
|
4817
|
-
console.error(`\u2716 Cannot read file: ${absInput}`);
|
|
4818
|
-
process.exit(1);
|
|
4819
|
-
}
|
|
4820
|
-
const result = compile(source);
|
|
4821
|
-
writeFileSync2(absOutput, result, "utf8");
|
|
4822
|
-
console.log(`\u2714 Compiled: ${inputPath} \u2192 ${absOutput}`);
|
|
4823
|
-
}
|
|
4824
|
-
function compile(source) {
|
|
4825
|
-
const tokens = new Lexer(source).tokenize();
|
|
4826
|
-
const ast = new Parser(tokens).parse();
|
|
4827
|
-
return generateRenderFunction(ast);
|
|
4828
|
-
}
|
|
4829
|
-
|
|
4830
|
-
// ../packages/cli/src/commands/compile/compile.ts
|
|
4831
|
-
function makeCompileCommand() {
|
|
4832
|
-
return new Command("compile").alias("co").description("Compile a Xendar HTML template into a render function").argument("<input>", "Path to the .xd.component.html template file").option("-o, --output <path>", "Path for the emitted output file (default: <input>.render.js)").action((input, options) => {
|
|
4833
|
-
compileFile(input, options.output);
|
|
4834
3789
|
});
|
|
4835
3790
|
}
|
|
4836
3791
|
|
|
4837
3792
|
// ../packages/cli/src/index.ts
|
|
4838
|
-
program.name("xd").description("
|
|
4839
|
-
program.addCommand(
|
|
4840
|
-
program.addCommand(
|
|
3793
|
+
program.name("xd").description("Xaendar CLI").version("0.2.0");
|
|
3794
|
+
program.addCommand(generateCommand());
|
|
3795
|
+
program.addCommand(newCommand());
|
|
4841
3796
|
program.parse();
|
|
4842
3797
|
//# sourceMappingURL=index.js.map
|