@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.
Files changed (3) hide show
  1. package/index.js +219 -1264
  2. package/index.js.map +1 -1
  3. 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: (a2, b) => (typeof require !== "undefined" ? require : a2)[b]
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((a2, b) => {
234
- return a2.name().localeCompare(b.name());
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(a2, b) {
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(a2).localeCompare(getSortKey(b));
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(a2, b) {
1117
- if (Math.abs(a2.length - b.length) > maxDistance)
1118
- return Math.max(a2.length, b.length);
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 <= a2.length; 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 <= a2.length; i++) {
1129
+ for (let i = 1; i <= a.length; i++) {
1128
1130
  let cost = 1;
1129
- if (a2[i - 1] === b[j - 1]) {
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 && a2[i - 1] === b[j - 2] && a2[i - 2] === b[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[a2.length][b.length];
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((a2, b) => a2.localeCompare(b));
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.css`, cssTemplate(name)],
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.css',
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 makeGenerateCommand() {
3552
- const generate = new Command("generate").alias("g").description("Generate Xendar building blocks");
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/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;
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/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
- };
3566
+ // ../packages/cli/src/commands/new/structure.ts
3567
+ import { readFileSync } from "fs";
3771
3568
 
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
- };
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/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
- };
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/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
- }
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
- function addCharacter(cursor, interpolation) {
4023
- cursor.advance(1);
4024
- return `${interpolation}${cursor.currentChar.value}`;
3621
+ `;
4025
3622
  }
4026
3623
 
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);
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
- return retVal;
3645
+ },
3646
+ "include": ["src/**/*.ts"],
3647
+ "exclude": ["dist", "node_modules", "**/*.spec.ts"]
4067
3648
  }
4068
- function addCharacter2(cursor, interpolation) {
4069
- cursor.advance(1);
4070
- return `${interpolation}${cursor.currentChar.value}`;
3649
+ `;
4071
3650
  }
4072
3651
 
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
- }
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
- // ../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;
3657
+ export default defineConfig({
3658
+ plugins: [xaendarPlugin()],
3659
+ });
3660
+ `;
4098
3661
  }
4099
3662
 
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;
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/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}`);
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
- // ../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}`;
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/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;
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
- // ../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;
3749
+ function resolveStyleOptions(style) {
3750
+ const validStyles = ["css", "scss", "less", "styl"];
3751
+ if (!style) {
3752
+ return "css";
4407
3753
  }
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
- }
3754
+ if (!validStyles.includes(style)) {
3755
+ console.error(`\u2716 Invalid style option: ${style}`);
3756
+ process.exit(1);
4487
3757
  }
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 };
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
- * 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 };
3766
+ if (!exists) {
3767
+ mkdirSync2(path);
4532
3768
  }
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`);
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
- 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}`);
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("Xendar CLI").version("0.2.0");
4839
- program.addCommand(makeGenerateCommand());
4840
- program.addCommand(makeCompileCommand());
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