juxscript 1.0.36 → 1.0.38

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.
@@ -4,6 +4,20 @@ import { BaseComponent } from './base/BaseComponent.js';
4
4
  const TRIGGER_EVENTS = [] as const;
5
5
  const CALLBACK_EVENTS = [] as const;
6
6
 
7
+ // Deprecation warning constant
8
+ const DEPRECATION_WARNING = (feature: string) =>
9
+ `[JUX Deprecation Warning] Container.${feature} will be removed in v1.0.30+ (January 30, 2026). Please use CSS or the .style() method instead. See: https://juxscript.com/docs/migration`;
10
+
11
+ // Track which warnings we've already shown (to avoid spam)
12
+ const _shownWarnings = new Set<string>();
13
+
14
+ function warnOnce(feature: string): void {
15
+ if (!_shownWarnings.has(feature)) {
16
+ console.warn(DEPRECATION_WARNING(feature));
17
+ _shownWarnings.add(feature);
18
+ }
19
+ }
20
+
7
21
  export interface ContainerOptions {
8
22
  direction?: 'row' | 'column'; // should deprecate.
9
23
  gap?: number | string; // should deprecate.
@@ -28,6 +42,14 @@ type ContainerState = {
28
42
 
29
43
  export class Container extends BaseComponent<ContainerState> {
30
44
  constructor(id: string, options: ContainerOptions = {}) {
45
+ // Warn for deprecated options
46
+ if (options.direction !== undefined) warnOnce('direction (option)');
47
+ if (options.gap !== undefined) warnOnce('gap (option)');
48
+ if (options.wrap !== undefined) warnOnce('wrap (option)');
49
+ if (options.align !== undefined) warnOnce('align (option)');
50
+ if (options.justify !== undefined) warnOnce('justify (option)');
51
+ if (options.padding !== undefined) warnOnce('padding (option)');
52
+
31
53
  super(id, {
32
54
  direction: options.direction ?? 'column',// should deprecate.
33
55
  gap: options.gap ?? 0, //should deprecate.
@@ -61,31 +83,37 @@ export class Container extends BaseComponent<ContainerState> {
61
83
  // - bind(), sync(), renderTo()
62
84
 
63
85
  direction(value: 'row' | 'column'): this {
86
+ warnOnce('direction()');
64
87
  this.state.direction = value;
65
88
  return this;
66
89
  }
67
90
 
68
91
  gap(value: number | string): this {
92
+ warnOnce('gap()');
69
93
  this.state.gap = value;
70
94
  return this;
71
95
  }
72
96
 
73
97
  wrap(value: boolean): this {
98
+ warnOnce('wrap()');
74
99
  this.state.wrap = value;
75
100
  return this;
76
101
  }
77
102
 
78
103
  align(value: 'start' | 'center' | 'end' | 'stretch'): this {
104
+ warnOnce('align()');
79
105
  this.state.align = value;
80
106
  return this;
81
107
  }
82
108
 
83
109
  justify(value: 'start' | 'center' | 'end' | 'space-between' | 'space-around' | 'space-evenly'): this {
110
+ warnOnce('justify()');
84
111
  this.state.justify = value;
85
112
  return this;
86
113
  }
87
114
 
88
115
  padding(value: string): this {
116
+ warnOnce('padding()');
89
117
  this.state.padding = value;
90
118
  return this;
91
119
  }
@@ -112,28 +140,34 @@ export class Container extends BaseComponent<ContainerState> {
112
140
 
113
141
  stateObj.subscribe((val: any) => {
114
142
  const transformed = transform(val);
115
- /*
143
+
116
144
  if (property === 'direction') {
145
+ warnOnce('direction (sync)');
117
146
  this.state.direction = String(transformed);
118
147
  wrapper.style.flexDirection = this.state.direction;
119
148
  } else if (property === 'gap') {
149
+ warnOnce('gap (sync)');
120
150
  this.state.gap = transformed;
121
151
  const gapVal = typeof transformed === 'number' ? `${transformed}px` : transformed;
122
152
  wrapper.style.gap = gapVal;
123
153
  } else if (property === 'wrap') {
154
+ warnOnce('wrap (sync)');
124
155
  this.state.wrap = Boolean(transformed);
125
156
  wrapper.style.flexWrap = this.state.wrap ? 'wrap' : 'nowrap';
126
157
  } else if (property === 'align') {
158
+ warnOnce('align (sync)');
127
159
  this.state.align = String(transformed);
128
160
  wrapper.style.alignItems = this.state.align;
129
161
  } else if (property === 'justify') {
162
+ warnOnce('justify (sync)');
130
163
  this.state.justify = String(transformed);
131
164
  wrapper.style.justifyContent = this.state.justify;
132
165
  } else if (property === 'padding') {
166
+ warnOnce('padding (sync)');
133
167
  this.state.padding = String(transformed);
134
168
  wrapper.style.padding = this.state.padding;
135
169
  }
136
- */
170
+
137
171
  });
138
172
 
139
173
  });
@@ -101,12 +101,6 @@
101
101
  "description": "List component",
102
102
  "constructor": "jux.list(id: string, options: ListOptions = {})",
103
103
  "fluentMethods": [
104
- {
105
- "name": "items",
106
- "params": "(value)",
107
- "returns": "this",
108
- "description": "Set items"
109
- },
110
104
  {
111
105
  "name": "addItem",
112
106
  "params": "(value)",
@@ -2059,5 +2053,5 @@
2059
2053
  }
2060
2054
  ],
2061
2055
  "version": "1.0.0",
2062
- "lastUpdated": "2026-01-28T15:24:08.460Z"
2056
+ "lastUpdated": "2026-01-28T17:24:41.869Z"
2063
2057
  }
@@ -419,38 +419,78 @@ export async function bundleJuxFilesToRouter(projectRoot, distDir, options = {})
419
419
  }
420
420
 
421
421
  /**
422
- * Extract shared module exports from a .jux file
423
- *
424
- * @param {string} juxContent - Original .jux file content
425
- * @param {string} moduleName - Module identifier
426
- * @returns {string} Shared module code
422
+ * Extract and replace all string literals with placeholders
423
+ * Handles: template literals WITHOUT interpolations, single quotes, double quotes
424
+ * SKIPS: Template literals WITH ${} interpolations (those are dynamic code)
427
425
  */
428
- function extractSharedModule(juxContent, moduleName) {
429
- // Remove string literals temporarily to avoid false matches
426
+ function extractStrings(code) {
430
427
  const strings = [];
428
+ let result = code;
429
+
430
+ // 1. Template literals WITHOUT interpolations (static strings only)
431
+ // Match backticks that DON'T contain ${
432
+ result = result.replace(/`([^`$\\]|\\[^$])*`/g, (match) => {
433
+ // Double-check it doesn't contain ${
434
+ if (!match.includes('${')) {
435
+ strings.push(match);
436
+ return `__STRING_${strings.length - 1}__`;
437
+ }
438
+ return match; // Leave dynamic templates alone
439
+ });
431
440
 
432
- // Handle template literals, single quotes, and double quotes separately
433
- let code = juxContent
434
- .replace(/`(?:\\.|[^`\\])*`/g, (match) => { strings.push(match); return `__STRING_${strings.length - 1}__`; })
435
- .replace(/"(?:\\.|[^"\\])*"/g, (match) => { strings.push(match); return `__STRING_${strings.length - 1}__`; })
436
- .replace(/'(?:\\.|[^'\\])*'/g, (match) => { strings.push(match); return `__STRING_${strings.length - 1}__`; });
441
+ // 2. Double-quoted strings (with escaped quotes)
442
+ result = result.replace(/"(?:[^"\\]|\\.)*"/g, (match) => {
443
+ strings.push(match);
444
+ return `__STRING_${strings.length - 1}__`;
445
+ });
437
446
 
438
- // Remove ALL imports - including _dev-imports.js
439
- code = code.replace(/import\s+\{[^}]+\}\s+from\s+__STRING_\d+__;?\s*/g, '');
440
- code = code.replace(/import\s+\*\s+as\s+\w+\s+from\s+__STRING_\d+__;?\s*/g, '');
441
- code = code.replace(/import\s+__STRING_\d+__;?\s*/g, '');
447
+ // 3. Single-quoted strings (with escaped quotes)
448
+ result = result.replace(/'(?:[^'\\]|\\.)*'/g, (match) => {
449
+ strings.push(match);
450
+ return `__STRING_${strings.length - 1}__`;
451
+ });
442
452
 
443
- // Convert exports to declarations (keep function keyword for functions)
444
- code = code.replace(/export\s+const\s+/g, 'const ');
445
- code = code.replace(/export\s+let\s+/g, 'let ');
446
- code = code.replace(/export\s+function\s+/g, 'function ');
447
- code = code.replace(/export\s+class\s+/g, 'class ');
448
- code = code.replace(/export\s+\{([^}]+)\}\s*;?\s*/g, '');
453
+ return { code: result, strings };
454
+ }
449
455
 
450
- // Restore string literals
451
- code = code.replace(/__STRING_(\d+)__/g, (_, index) => strings[index]);
456
+ /**
457
+ * Restore string placeholders back to original strings
458
+ */
459
+ function restoreStrings(code, strings) {
460
+ return code.replace(/__STRING_(\d+)__/g, (match, index) => {
461
+ const idx = parseInt(index, 10);
462
+ if (idx >= 0 && idx < strings.length) {
463
+ return strings[idx];
464
+ }
465
+ console.warn(`[Compiler] String placeholder ${match} not found (idx: ${idx}, available: ${strings.length})`);
466
+ return match; // Leave as-is for debugging
467
+ });
468
+ }
452
469
 
453
- return code;
470
+ /**
471
+ * Extract shared module exports from a .jux file
472
+ *
473
+ * @param {string} juxContent - Original .jux file content
474
+ * @param {string} moduleName - Module identifier
475
+ * @returns {string} Shared module code
476
+ */
477
+ function extractSharedModule(juxContent, moduleName) {
478
+ const { code, strings } = extractStrings(juxContent);
479
+
480
+ // Remove ALL imports
481
+ let result = code.replace(/import\s+\{[^}]+\}\s+from\s+__STRING_\d+__;?\s*/g, '');
482
+ result = result.replace(/import\s+\*\s+as\s+\w+\s+from\s+__STRING_\d+__;?\s*/g, '');
483
+ result = result.replace(/import\s+__STRING_\d+__;?\s*/g, '');
484
+
485
+ // Convert exports to declarations
486
+ result = result.replace(/export\s+const\s+/g, 'const ');
487
+ result = result.replace(/export\s+let\s+/g, 'let ');
488
+ result = result.replace(/export\s+function\s+/g, 'function ');
489
+ result = result.replace(/export\s+class\s+/g, 'class ');
490
+ result = result.replace(/export\s+\{([^}]+)\}\s*;?\s*/g, '');
491
+
492
+ // Restore strings
493
+ return restoreStrings(result, strings);
454
494
  }
455
495
 
456
496
  /**
@@ -464,36 +504,27 @@ function extractSharedModule(juxContent, moduleName) {
464
504
  * @returns {string} View function code
465
505
  */
466
506
  function transformJuxToViewFunction(juxContent, functionName, pageName, relativePath, sharedModules) {
467
- // Remove string literals temporarily to avoid false matches
468
- const strings = [];
469
-
470
- // Handle template literals, single quotes, and double quotes separately
471
- let code = juxContent
472
- .replace(/`(?:\\.|[^`\\])*`/g, (match) => { strings.push(match); return `__STRING_${strings.length - 1}__`; })
473
- .replace(/"(?:\\.|[^"\\])*"/g, (match) => { strings.push(match); return `__STRING_${strings.length - 1}__`; })
474
- .replace(/'(?:\\.|[^'\\])*'/g, (match) => { strings.push(match); return `__STRING_${strings.length - 1}__`; });
475
-
476
- // Remove ALL imports - now matching against placeholders
477
- code = code.replace(/import\s+\{[^}]+\}\s+from\s+__STRING_\d+__;?\s*/g, '');
478
- code = code.replace(/import\s+\*\s+as\s+\w+\s+from\s+__STRING_\d+__;?\s*/g, '');
479
- code = code.replace(/import\s+__STRING_\d+__;?\s*/g, '');
507
+ const { code, strings } = extractStrings(juxContent);
480
508
 
481
- // Handle exports - convert named exports to const declarations
482
- code = code.replace(/export\s+const\s+(\w+)\s*=/g, 'const $1 =');
483
- code = code.replace(/export\s+let\s+(\w+)\s*=/g, 'let $1 =');
484
- code = code.replace(/export\s+function\s+(\w+)/g, 'const $1 = function');
485
- code = code.replace(/export\s+class\s+/g, 'class ');
486
- code = code.replace(/export\s+default\s+/g, '');
487
- code = code.replace(/export\s+\{([^}]+)\}\s*;?\s*/g, '');
509
+ // Remove ALL imports
510
+ let result = code.replace(/import\s+\{[^}]+\}\s+from\s+__STRING_\d+__;?\s*/g, '');
511
+ result = result.replace(/import\s+\*\s+as\s+\w+\s+from\s+__STRING_\d+__;?\s*/g, '');
512
+ result = result.replace(/import\s+__STRING_\d+__;?\s*/g, '');
488
513
 
489
- // Only replace .renderTo(container) calls - leave explicit render targets alone
490
- code = code.replace(/\.renderTo\(container\)/g, '.render("#app")');
514
+ // Handle exports
515
+ result = result.replace(/export\s+const\s+(\w+)\s*=/g, 'const $1 =');
516
+ result = result.replace(/export\s+let\s+(\w+)\s*=/g, 'let $1 =');
517
+ result = result.replace(/export\s+function\s+(\w+)/g, 'const $1 = function');
518
+ result = result.replace(/export\s+class\s+/g, 'class ');
519
+ result = result.replace(/export\s+default\s+/g, '');
520
+ result = result.replace(/export\s+\{([^}]+)\}\s*;?\s*/g, '');
491
521
 
492
- // Only replace empty .render() calls (no arguments)
493
- code = code.replace(/\.render\(\s*\)/g, '.render("#app")');
522
+ // Replace render patterns
523
+ result = result.replace(/\.renderTo\(container\)/g, '.render("#app")');
524
+ result = result.replace(/\.render\(\s*\)/g, '.render("#app")');
494
525
 
495
- // Restore string literals
496
- code = code.replace(/__STRING_(\d+)__/g, (_, index) => strings[index]);
526
+ // Restore strings
527
+ result = restoreStrings(result, strings);
497
528
 
498
529
  const cleanName = functionName
499
530
  .replace(/[-_]/g, ' ')
@@ -504,7 +535,7 @@ function transformJuxToViewFunction(juxContent, functionName, pageName, relative
504
535
  return `
505
536
  // View: ${cleanName}
506
537
  function ${cleanName}() {
507
- ${code}
538
+ ${result}
508
539
 
509
540
  return document.getElementById('app');
510
541
  }`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.0.36",
3
+ "version": "1.0.38",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "lib/jux.js",