@xaendar/cli 0.2.0 → 0.3.0

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 (3) hide show
  1. package/index.js +217 -1264
  2. package/index.js.map +1 -1
  3. package/package.json +2 -2
package/index.js CHANGED
@@ -6,7 +6,7 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
8
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
9
- get: (a2, b) => (typeof require !== "undefined" ? require : a2)[b]
9
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
10
10
  }) : x)(function(x) {
11
11
  if (typeof require !== "undefined") return require.apply(this, arguments);
12
12
  throw Error('Dynamic require of "' + x + '" is not supported');
@@ -230,8 +230,8 @@ var require_help = __commonJS({
230
230
  visibleCommands.push(helpCommand);
231
231
  }
232
232
  if (this.sortSubcommands) {
233
- visibleCommands.sort((a2, b) => {
234
- return a2.name().localeCompare(b.name());
233
+ visibleCommands.sort((a, b) => {
234
+ return a.name().localeCompare(b.name());
235
235
  });
236
236
  }
237
237
  return visibleCommands;
@@ -243,11 +243,11 @@ var require_help = __commonJS({
243
243
  * @param {Option} b
244
244
  * @returns {number}
245
245
  */
246
- compareOptions(a2, b) {
246
+ compareOptions(a, b) {
247
247
  const getSortKey = (option) => {
248
248
  return option.short ? option.short.replace(/^-/, "") : option.long.replace(/^--/, "");
249
249
  };
250
- return getSortKey(a2).localeCompare(getSortKey(b));
250
+ return getSortKey(a).localeCompare(getSortKey(b));
251
251
  }
252
252
  /**
253
253
  * Get an array of the visible options. Includes a placeholder for the implicit help option, if there is one.
@@ -1113,20 +1113,20 @@ var require_option = __commonJS({
1113
1113
  var require_suggestSimilar = __commonJS({
1114
1114
  "../node_modules/commander/lib/suggestSimilar.js"(exports) {
1115
1115
  var maxDistance = 3;
1116
- function editDistance(a2, b) {
1117
- if (Math.abs(a2.length - b.length) > maxDistance)
1118
- return Math.max(a2.length, b.length);
1116
+ function editDistance(a, b) {
1117
+ if (Math.abs(a.length - b.length) > maxDistance)
1118
+ return Math.max(a.length, b.length);
1119
1119
  const d = [];
1120
- for (let i = 0; i <= a2.length; i++) {
1120
+ for (let i = 0; i <= a.length; i++) {
1121
1121
  d[i] = [i];
1122
1122
  }
1123
1123
  for (let j = 0; j <= b.length; j++) {
1124
1124
  d[0][j] = j;
1125
1125
  }
1126
1126
  for (let j = 1; j <= b.length; j++) {
1127
- for (let i = 1; i <= a2.length; i++) {
1127
+ for (let i = 1; i <= a.length; i++) {
1128
1128
  let cost = 1;
1129
- if (a2[i - 1] === b[j - 1]) {
1129
+ if (a[i - 1] === b[j - 1]) {
1130
1130
  cost = 0;
1131
1131
  } else {
1132
1132
  cost = 1;
@@ -1139,12 +1139,12 @@ var require_suggestSimilar = __commonJS({
1139
1139
  d[i - 1][j - 1] + cost
1140
1140
  // substitution
1141
1141
  );
1142
- if (i > 1 && j > 1 && a2[i - 1] === b[j - 2] && a2[i - 2] === b[j - 1]) {
1142
+ if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
1143
1143
  d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1);
1144
1144
  }
1145
1145
  }
1146
1146
  }
1147
- return d[a2.length][b.length];
1147
+ return d[a.length][b.length];
1148
1148
  }
1149
1149
  function suggestSimilar(word, candidates) {
1150
1150
  if (!candidates || candidates.length === 0) return "";
@@ -1171,7 +1171,7 @@ var require_suggestSimilar = __commonJS({
1171
1171
  }
1172
1172
  }
1173
1173
  });
1174
- similar.sort((a2, b) => a2.localeCompare(b));
1174
+ similar.sort((a, b) => a.localeCompare(b));
1175
1175
  if (searchingOptions) {
1176
1176
  similar = similar.map((candidate) => `--${candidate}`);
1177
1177
  }
@@ -3477,8 +3477,9 @@ var {
3477
3477
  // ../packages/cli/src/commands/generate/component/component.command.ts
3478
3478
  import { mkdirSync, writeFileSync, existsSync, rmSync } from "fs";
3479
3479
  import { join } from "path";
3480
- function generateComponent(name, path, force) {
3480
+ function generateComponent(name, path, force, style) {
3481
3481
  const dir = join(path, name);
3482
+ style ??= "css";
3482
3483
  if (existsSync(dir)) {
3483
3484
  if (force) {
3484
3485
  console.log(`Deleting "${name}"...`);
@@ -3491,9 +3492,9 @@ function generateComponent(name, path, force) {
3491
3492
  }
3492
3493
  mkdirSync(dir, { recursive: true });
3493
3494
  const files = [
3494
- [`${name}.xd.component.ts`, tsTemplate(name)],
3495
+ [`${name}.xd.component.ts`, tsTemplate(name, style)],
3495
3496
  [`${name}.xd.component.html`, htmlTemplate(name)],
3496
- [`${name}.xd.component.css`, cssTemplate(name)],
3497
+ [`${name}.xd.component.${style}`, cssTemplate(name)],
3497
3498
  [`${name}.xd.component.spec.ts`, specTemplate(name)]
3498
3499
  ];
3499
3500
  for (const [filename, content] of files) {
@@ -3505,13 +3506,13 @@ function generateComponent(name, path, force) {
3505
3506
  console.log(`${name}/${filename}`);
3506
3507
  }
3507
3508
  }
3508
- function tsTemplate(name) {
3509
+ function tsTemplate(name, style) {
3509
3510
  const className = toPascalCase(name);
3510
3511
  return `import { BaseWebComponent, WebComponent } from '@xaendar/core';
3511
3512
 
3512
3513
  @WebComponent({
3513
3514
  selector: '${toKebabCase(name)}',
3514
- styleUrl: './${name}.xd.component.css',
3515
+ styleUrl: './${name}.xd.component.${style}',
3515
3516
  templateUrl: './${name}.xd.component.html'
3516
3517
  })
3517
3518
  export class ${className}Component extends BaseWebComponent {
@@ -3528,8 +3529,7 @@ function cssTemplate(_name) {
3528
3529
  }
3529
3530
  function specTemplate(name) {
3530
3531
  const className = toPascalCase(name);
3531
- return `
3532
- import { describe, expect, it } from "vitest";
3532
+ return `import { describe, expect, it } from "vitest";
3533
3533
  import { ${className}Component } from './${name}.xd.component';
3534
3534
 
3535
3535
  describe('${className}Component', () => {
@@ -3548,8 +3548,8 @@ function toKebabCase(str) {
3548
3548
  }
3549
3549
 
3550
3550
  // ../packages/cli/src/commands/generate/generate.command.ts
3551
- function makeGenerateCommand() {
3552
- const generate = new Command("generate").alias("g").description("Generate Xendar building blocks");
3551
+ function generateCommand() {
3552
+ const generate = new Command("generate").alias("g").description("Generate Xaendar building blocks");
3553
3553
  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
3554
  const path = options.path || process.cwd();
3555
3555
  generateComponent(name, path, !!options.force);
@@ -3557,1286 +3557,239 @@ function makeGenerateCommand() {
3557
3557
  return generate;
3558
3558
  }
3559
3559
 
3560
- // ../packages/common/src/models/stack/stack.model.ts
3561
- var Stack = class {
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;
3560
+ // ../packages/cli/src/commands/new/new.command.ts
3561
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, writeFileSync as writeFileSync2 } from "fs";
3562
+ import { resolve } from "path/win32";
3618
3563
 
3619
- // ../packages/compiler/src/lexer/models/lexer-cursor.model.ts
3620
- var LexerCursor = class {
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
- };
3564
+ // ../packages/cli/src/commands/new/structure.ts
3565
+ import { readFileSync } from "fs";
3771
3566
 
3772
- // ../packages/compiler/src/lexer/models/token-type.enum.ts
3773
- var TokenType = /* @__PURE__ */ ((TokenType2) => {
3774
- TokenType2[TokenType2["TEXT"] = 0] = "TEXT";
3775
- TokenType2[TokenType2["TAG_OPEN_NAME"] = 1] = "TAG_OPEN_NAME";
3776
- TokenType2[TokenType2["TAG_SELF_CLOSE"] = 2] = "TAG_SELF_CLOSE";
3777
- TokenType2[TokenType2["TAG_OPEN_END"] = 3] = "TAG_OPEN_END";
3778
- TokenType2[TokenType2["TAG_CLOSE_NAME"] = 4] = "TAG_CLOSE_NAME";
3779
- TokenType2[TokenType2["ATTRIBUTE"] = 5] = "ATTRIBUTE";
3780
- TokenType2[TokenType2["EVENT"] = 6] = "EVENT";
3781
- TokenType2[TokenType2["INTERPOLATION_LITERAL"] = 7] = "INTERPOLATION_LITERAL";
3782
- TokenType2[TokenType2["INTERPOLATION_EXPRESSION"] = 8] = "INTERPOLATION_EXPRESSION";
3783
- TokenType2[TokenType2["IF"] = 9] = "IF";
3784
- TokenType2[TokenType2["FOR"] = 10] = "FOR";
3785
- TokenType2[TokenType2["ELSE"] = 11] = "ELSE";
3786
- TokenType2[TokenType2["SWITCH"] = 12] = "SWITCH";
3787
- TokenType2[TokenType2["CASE"] = 13] = "CASE";
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
- };
3567
+ // ../packages/cli/src/commands/new/templates/index-html.ts
3568
+ function indexHtml(name) {
3569
+ const selector = `${name}-root`;
3570
+ return `<!DOCTYPE html>
3571
+ <html lang="en">
3572
+ <head>
3573
+ <meta charset="UTF-8" />
3574
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
3575
+ <title>${name}</title>
3576
+ </head>
3577
+ <body>
3578
+ <${selector}></${selector}>
3579
+ <script type="module" src="/src/main.ts"></script>
3580
+ </body>
3581
+ </html>
3582
+ `;
3936
3583
  }
3937
3584
 
3938
- // ../packages/compiler/src/lexer/states/flow-control-condition.state.ts
3939
- function consumeFlowControlCondition(cursor, _context) {
3940
- cursor.skipSpaces();
3941
- if (cursor.peek() !== LPAREN) {
3942
- throw new Error(`Expected '(' but got '${String.fromCharCode(cursor.peek())}' at row ${cursor.position.row}, col ${cursor.position.column}`);
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
- };
3585
+ // ../packages/cli/src/commands/new/templates/main-ts.ts
3586
+ function mainTs(componentName) {
3587
+ return `import { loadSignals } from '@xaendar/signals';
3588
+
3589
+ loadSignals();
3590
+ `;
3975
3591
  }
3976
3592
 
3977
- // ../packages/compiler/src/lexer/states/interpolation-expression.state.ts
3978
- function consumeInterpolationExpression(cursor, context) {
3979
- let read = true;
3980
- let interpolation = "";
3981
- let deep = 1;
3982
- let retVal;
3983
- while (read) {
3984
- switch (cursor.peek()) {
3985
- case LEFT_BRACE:
3986
- deep++;
3987
- interpolation = addCharacter(cursor, interpolation);
3988
- break;
3989
- case RIGHT_BRACE:
3990
- deep--;
3991
- if (deep === 0) {
3992
- cursor.advance();
3993
- const previousState = context.history.pop();
3994
- let state;
3995
- switch (previousState) {
3996
- case "attribute" /* ATTRIBUTE */:
3997
- state = "tag-body" /* TAG_BODY */;
3998
- break;
3999
- case "text" /* TEXT */:
4000
- state = "text" /* TEXT */;
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
- }
3593
+ // ../packages/cli/src/commands/new/templates/package-json.ts
3594
+ function packageJson(name, version) {
3595
+ return `{
3596
+ "name": "${name}",
3597
+ "version": "0.0.1",
3598
+ "private": true,
3599
+ "scripts": {
3600
+ "build": "vite build",
3601
+ "start": "vite",
3602
+ "test": "vitest",
3603
+ "xd": "xd"
3604
+ },
3605
+ "dependencies": {
3606
+ "@xaendar/core": "^${version}",
3607
+ "@xaendar/signals": "^${version}",
3608
+ "@xaendar/types": "^${version}"
3609
+ },
3610
+ "devDependencies": {
3611
+ "@vitest/coverage-v8": "^4.1.7",
3612
+ "@xaendar/build-tools": "^${version}",
3613
+ "@xaendar/cli": "^${version}",
3614
+ "typescript": "^6.0.3",
3615
+ "vite": "^8.0.14",
3616
+ "vitest": "^4.1.7"
4019
3617
  }
4020
- return retVal;
4021
3618
  }
4022
- function addCharacter(cursor, interpolation) {
4023
- cursor.advance(1);
4024
- return `${interpolation}${cursor.currentChar.value}`;
3619
+ `;
4025
3620
  }
4026
3621
 
4027
- // ../packages/compiler/src/lexer/states/interpolation-literal.state.ts
4028
- function consumeInterpolationliteral(cursor, context) {
4029
- let read = true;
4030
- let interpolation = "";
4031
- let retVal;
4032
- cursor.advance();
4033
- while (read) {
4034
- switch (cursor.peek()) {
4035
- case GRAVE_ACCENT:
4036
- cursor.advance();
4037
- if (cursor.peek() === RIGHT_BRACE) {
4038
- cursor.advance();
4039
- const previousState = context.history.pop();
4040
- let state;
4041
- switch (previousState) {
4042
- case "attribute" /* ATTRIBUTE */:
4043
- state = "tag-body" /* TAG_BODY */;
4044
- break;
4045
- case "text" /* TEXT */:
4046
- state = "text" /* TEXT */;
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);
3622
+ // ../packages/cli/src/commands/new/templates/tsconfig-json.ts
3623
+ function tsconfigJson() {
3624
+ return `{
3625
+ "compilerOptions": {
3626
+ "target": "ESNext",
3627
+ "module": "ESNext",
3628
+ "moduleResolution": "bundler",
3629
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
3630
+ "outDir": "./dist",
3631
+ "rootDir": "./src",
3632
+ "declaration": true,
3633
+ "declarationMap": true,
3634
+ "sourceMap": true,
3635
+ "isolatedModules": true,
3636
+ "moduleDetection": "force",
3637
+ "noUncheckedIndexedAccess": true,
3638
+ "noEmit": true,
3639
+ "skipLibCheck": true,
3640
+ "paths": {
3641
+ "@/*": ["./src/*"]
4064
3642
  }
4065
- }
4066
- return retVal;
3643
+ },
3644
+ "include": ["src/**/*.ts"],
3645
+ "exclude": ["dist", "node_modules", "**/*.spec.ts"]
4067
3646
  }
4068
- function addCharacter2(cursor, interpolation) {
4069
- cursor.advance(1);
4070
- return `${interpolation}${cursor.currentChar.value}`;
3647
+ `;
4071
3648
  }
4072
3649
 
4073
- // ../packages/compiler/src/utils/chars.utils.ts
4074
- function isNotBlank(str) {
4075
- return /\S/.test(str);
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
- }
3650
+ // ../packages/cli/src/commands/new/templates/vite-config-ts.ts
3651
+ function viteConfigTs() {
3652
+ return `import { defineConfig } from 'vite';
3653
+ import { xaendarPlugin } from '@xaendar/build-tools';
4083
3654
 
4084
- // ../packages/compiler/src/lexer/states/interpolation.state.ts
4085
- function consumeInterpolation(cursor, _context) {
4086
- let retVal;
4087
- cursor.advance();
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;
3655
+ export default defineConfig({
3656
+ plugins: [xaendarPlugin()],
3657
+ });
3658
+ `;
4098
3659
  }
4099
3660
 
4100
- // ../packages/compiler/src/lexer/states/tag-body.state.ts
4101
- function consumeTagBody(cursor, _context) {
4102
- let read = true;
4103
- let retVal;
4104
- while (read) {
4105
- const nextChar = cursor.peek();
4106
- switch (nextChar) {
4107
- case AT_SIGN:
4108
- retVal = {
4109
- state: "event" /* EVENT */
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;
3661
+ // ../packages/cli/src/commands/new/templates/xaendar-json.ts
3662
+ function xaendarJson(name, style) {
3663
+ return `{
3664
+ "name": "${name}",
3665
+ "entry": "src/main.ts",
3666
+ "outDir": "dist",
3667
+ "assetsDir": "assets",
3668
+ "generate": {
3669
+ "components": {
3670
+ "style": "${style}"
4128
3671
  }
4129
3672
  }
4130
- return retVal;
4131
3673
  }
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;
3674
+ `;
4161
3675
  }
4162
3676
 
4163
- // ../packages/compiler/src/lexer/states/tag-open-end.state.ts
4164
- function consumeTagOpenEnd(cursor, _context) {
4165
- let retVal;
4166
- if (cursor.peek() === GREATER_THEN) {
4167
- cursor.advance();
4168
- retVal = {
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}`);
3677
+ // ../packages/cli/src/commands/new/structure.ts
3678
+ function readCliVersion() {
3679
+ try {
3680
+ const cliPackageJson = JSON.parse(readFileSync("./package.json", "utf8"));
3681
+ if (!cliPackageJson.version) {
3682
+ throw new Error("Unable to determine Xaendar CLI version.");
4192
3683
  }
3684
+ return cliPackageJson.version;
3685
+ } catch (error) {
3686
+ console.error("Error reading Xaendar CLI version:", error);
3687
+ process.exit(1);
4193
3688
  }
4194
- return retVal;
4195
3689
  }
4196
-
4197
- // ../packages/compiler/src/lexer/states/tag-open-name.state.ts
4198
- function consumeTagOpenName(cursor, _context) {
4199
- let read = true;
4200
- let tagName = "";
4201
- let retVal;
4202
- cursor.advance();
4203
- cursor.skipSpaces();
4204
- while (read) {
4205
- switch (cursor.peek()) {
4206
- case SPACE:
4207
- case SLASH:
4208
- case GREATER_THEN:
4209
- retVal = {
4210
- state: "tag-body" /* TAG_BODY */,
4211
- tokens: [{
4212
- type: 1 /* TAG_OPEN_NAME */,
4213
- parts: [tagName]
4214
- }]
4215
- };
4216
- read = false;
4217
- break;
4218
- default:
4219
- cursor.advance();
4220
- tagName = `${tagName}${cursor.currentChar.value}`;
3690
+ function buildStructure(context) {
3691
+ const version = readCliVersion();
3692
+ const componentName = context.name;
3693
+ return [
3694
+ {
3695
+ type: "file",
3696
+ name: "package.json",
3697
+ content: packageJson(componentName, version)
3698
+ },
3699
+ {
3700
+ type: "file",
3701
+ name: "xaendar.json",
3702
+ content: xaendarJson(componentName, context.style)
3703
+ },
3704
+ {
3705
+ type: "file",
3706
+ name: "vite.config.ts",
3707
+ content: viteConfigTs()
3708
+ },
3709
+ {
3710
+ type: "file",
3711
+ name: "tsconfig.json",
3712
+ content: tsconfigJson()
3713
+ },
3714
+ {
3715
+ type: "directory",
3716
+ name: "src",
3717
+ children: [
3718
+ {
3719
+ type: "file",
3720
+ name: "index.html",
3721
+ content: indexHtml(componentName)
3722
+ },
3723
+ {
3724
+ type: "file",
3725
+ name: "main.ts",
3726
+ content: mainTs(componentName)
3727
+ },
3728
+ {
3729
+ type: "generateComponent",
3730
+ name: componentName
3731
+ }
3732
+ ]
4221
3733
  }
4222
- }
4223
- return retVal;
3734
+ ];
4224
3735
  }
4225
3736
 
4226
- // ../packages/compiler/src/lexer/states/text.state.ts
4227
- function consumeText(cursor, context) {
4228
- let read = true;
4229
- let text = "";
4230
- let retVal;
4231
- while (read) {
4232
- switch (cursor.peek()) {
4233
- case LESS_THAN:
4234
- const nextState = cursor.peek(1, { offset: 1 }) === SLASH ? "tag-close" /* TAG_CLOSE */ : "tag-open-name" /* TAG_OPEN_NAME */;
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;
3737
+ // ../packages/cli/src/commands/new/new.command.ts
3738
+ function newCommand() {
3739
+ 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) => {
3740
+ const style = resolveStyleOptions(options.style);
3741
+ const workingDirectory = options.path || process.cwd();
3742
+ const projectDirectory = resolve(workingDirectory, name);
3743
+ checkAndCreateProjectDirectory(projectDirectory);
3744
+ createFiles(buildStructure({ name, style }), projectDirectory, style);
3745
+ });
4276
3746
  }
4277
-
4278
- // ../packages/compiler/src/lexer/lexer.ts
4279
- var Lexer = class {
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;
3747
+ function resolveStyleOptions(style) {
3748
+ const validStyles = ["css", "scss", "less", "styl"];
3749
+ if (!style) {
3750
+ return "css";
4407
3751
  }
4408
- /**
4409
- * Peeks a single token at the given absolute index.
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
- }
3752
+ if (!validStyles.includes(style)) {
3753
+ console.error(`\u2716 Invalid style option: ${style}`);
3754
+ process.exit(1);
4487
3755
  }
4488
- /**
4489
- * Parses an @if block, including an optional @else branch.
4490
- *
4491
- * Expected token sequence:
4492
- * IF CONDITION BLOCK_OPEN → ...children... → BLOCK_CLOSE
4493
- * (optionally followed by: ELSE BLOCK_OPEN ...children... → BLOCK_CLOSE)
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 };
3756
+ return style;
3757
+ }
3758
+ function checkAndCreateProjectDirectory(path) {
3759
+ const exists = existsSync2(path);
3760
+ if (exists && readdirSync(path).length) {
3761
+ console.error(`\u2716 Cannot create project: target directory is not empty: ${path}`);
3762
+ process.exit(1);
4514
3763
  }
4515
- /**
4516
- * Parses a @for block.
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 };
3764
+ if (!exists) {
3765
+ mkdirSync2(path);
4532
3766
  }
4533
- /**
4534
- * Parses a @switch block containing @case and @default branches.
4535
- *
4536
- * Expected token sequence:
4537
- * SWITCH → CONDITION → BLOCK_OPEN
4538
- * (CASE CONDITION → BLOCK_OPEN → ...children... → BLOCK_CLOSE)*
4539
- * (DEFAULT BLOCK_OPEN → ...children... → BLOCK_CLOSE)?
4540
- * BLOCK_CLOSE
4541
- */
4542
- parseSwitchControlFlow(_token) {
4543
- this._cursor.advance();
4544
- const conditionToken = this._cursor.peek();
4545
- if (conditionToken.type !== 15 /* CONDITION */) {
4546
- throw this.error(`Expected CONDITION after SWITCH, got ${TokenType[conditionToken.type]}`);
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`);
3767
+ }
3768
+ function createFiles(entries, basePath, style) {
3769
+ entries.forEach((entry) => {
3770
+ switch (entry.type) {
3771
+ case "file":
3772
+ case void 0:
3773
+ const content = entry.content;
3774
+ writeFileSync2(resolve(basePath, entry.name), content, "utf8");
3775
+ break;
3776
+ case "directory":
3777
+ const dirPath = resolve(basePath, entry.name);
3778
+ mkdirSync2(dirPath);
3779
+ if (entry.children) {
3780
+ createFiles(entry.children, dirPath, style);
4559
3781
  }
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
3782
  break;
4570
- }
4571
- }
4572
- this._cursor.advance();
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}`);
3783
+ case "generateComponent":
3784
+ generateComponent(entry.name, basePath, false, style);
3785
+ break;
4691
3786
  }
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
3787
  });
4835
3788
  }
4836
3789
 
4837
3790
  // ../packages/cli/src/index.ts
4838
- program.name("xd").description("Xendar CLI").version("0.2.0");
4839
- program.addCommand(makeGenerateCommand());
4840
- program.addCommand(makeCompileCommand());
3791
+ program.name("xd").description("Xaendar CLI").version("0.2.0");
3792
+ program.addCommand(generateCommand());
3793
+ program.addCommand(newCommand());
4841
3794
  program.parse();
4842
3795
  //# sourceMappingURL=index.js.map