@portel/photon-core 2.5.3 → 2.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../src/compiler.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAiEH,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9C,OAAO,CAAC,MAAM,CAAC,CAmCjB"}
1
+ {"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../src/compiler.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA2GH,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9C,OAAO,CAAC,MAAM,CAAC,CAmCjB"}
package/dist/compiler.js CHANGED
@@ -19,40 +19,74 @@ import * as crypto from 'crypto';
19
19
  * @returns Absolute path to the compiled .mjs file
20
20
  */
21
21
  /**
22
- * Transform array/map/set literals to constructor calls when using reactive imports
22
+ * Transform arrays to reactive collections for PhotonMCP classes
23
23
  *
24
- * When a file imports { Array } from '@portel/photon-core', transforms:
25
- * items: Array<Task> = []; → items: Array<Task> = new Array();
26
- * items = []; → items = new Array();
27
- * data: Map<K,V> = new Map(); → (unchanged, already correct)
24
+ * ZERO-EFFORT REACTIVITY: If a class extends PhotonMCP and has array properties,
25
+ * this transform automatically:
26
+ * 1. Injects `import { Array as ReactiveArray } from '@portel/photon-core'`
27
+ * 2. Transforms `= []` to `= new ReactiveArray()` for class properties
28
28
  *
29
- * Only transforms class properties (not local variables like const x = []).
30
- * This enables zero-effort reactivity where developers just import and use.
29
+ * Result: Developers write normal code, arrays are automatically reactive.
30
+ *
31
+ * ```typescript
32
+ * // Developer writes this (normal TypeScript):
33
+ * export default class TodoList extends PhotonMCP {
34
+ * items: Task[] = [];
35
+ * async add(text: string) { this.items.push({...}); }
36
+ * }
37
+ *
38
+ * // Compiler transforms to:
39
+ * import { Array as ReactiveArray } from '@portel/photon-core';
40
+ * export default class TodoList extends PhotonMCP {
41
+ * items = new ReactiveArray();
42
+ * async add(text: string) { this.items.push({...}); } // Auto-emits!
43
+ * }
44
+ * ```
31
45
  */
32
46
  function transformReactiveCollections(source) {
33
- // Check which reactive types are imported
34
- const importMatch = source.match(/import\s*\{([^}]+)\}\s*from\s*['"]@portel\/photon-core['"]/);
35
- if (!importMatch)
47
+ // Check if this is a PhotonMCP class (extends PhotonMCP)
48
+ const isPhotonMCP = /class\s+\w+\s+extends\s+PhotonMCP\b/.test(source);
49
+ if (!isPhotonMCP)
50
+ return source;
51
+ // Check if there are array properties with = [] that need transformation
52
+ // Look for patterns like: `items: Type[] = []` or `items = []` (class properties)
53
+ const hasArrayLiterals = /\w+\s*:\s*[^=\n]+\[\]\s*=\s*\[\s*\]/.test(source) || // typed: Type[] = []
54
+ /^\s+\w+\s*=\s*\[\s*\](?=\s*[;\n])/m.test(source); // simple: prop = []
55
+ if (!hasArrayLiterals)
36
56
  return source;
37
- const imports = importMatch[1].split(',').map(s => s.trim());
38
57
  let transformed = source;
39
- // Transform [] to new Array() if Array is imported
40
- if (imports.includes('Array')) {
41
- // Match class property declarations with type annotation = []
42
- // Handles: items: Array<T> = []; | items: Type[] = [];
43
- // The (?::\s*...) ensures there's a type annotation (class property pattern)
44
- transformed = transformed.replace(/(\w+)\s*:\s*(?:Array<[^>]+>|[^=\n]+\[\])\s*=\s*\[\s*\]/g, '$1 = new Array()');
45
- // Match class property without type annotation but NOT local variables
46
- // Class properties: ` items = [];` (indented, no const/let/var)
47
- // Skip: `const x = []`, `let x = []`, `var x = []`
48
- transformed = transformed.replace(/^(\s+)(\w+)\s*=\s*\[\s*\](?=\s*[;\n])/gm, (match, indent, propName) => {
49
- // Check if previous non-empty line contains const/let/var - if so, skip
50
- // This is a heuristic but works for common patterns
51
- return `${indent}${propName} = new Array()`;
52
- });
58
+ // Check if Array is already imported from photon-core
59
+ const hasArrayImport = /import\s*\{[^}]*\bArray\b[^}]*\}\s*from\s*['"]@portel\/photon-core['"]/.test(source);
60
+ if (!hasArrayImport) {
61
+ // Inject ReactiveArray import (using alias to avoid shadowing issues)
62
+ // Find the photon-core import and add ReactiveArray to it, or add new import
63
+ const photonCoreImport = source.match(/import\s*\{([^}]+)\}\s*from\s*['"]@portel\/photon-core['"]/);
64
+ if (photonCoreImport) {
65
+ // Add to existing import
66
+ const existingImports = photonCoreImport[1];
67
+ transformed = transformed.replace(photonCoreImport[0], `import { ${existingImports}, Array as ReactiveArray } from '@portel/photon-core'`);
68
+ }
69
+ else {
70
+ // Add new import at the top (after any existing imports)
71
+ const lastImportMatch = source.match(/^import\s+.+$/gm);
72
+ if (lastImportMatch) {
73
+ const lastImport = lastImportMatch[lastImportMatch.length - 1];
74
+ transformed = transformed.replace(lastImport, `${lastImport}\nimport { Array as ReactiveArray } from '@portel/photon-core';`);
75
+ }
76
+ else {
77
+ // No imports, add at top
78
+ transformed = `import { Array as ReactiveArray } from '@portel/photon-core';\n${transformed}`;
79
+ }
80
+ }
53
81
  }
54
- // Map and Set already require new, so no transform needed
55
- // (you can't write = {} for Map or Set literals)
82
+ // Determine the Array constructor name to use
83
+ const arrayConstructor = hasArrayImport ? 'Array' : 'ReactiveArray';
84
+ // Transform class property declarations with type annotation = []
85
+ // Handles: items: Task[] = []; | items: Array<T> = [];
86
+ transformed = transformed.replace(/(\w+)\s*:\s*(?:Array<[^>]+>|[^=\n]+\[\])\s*=\s*\[\s*\]/g, `$1 = new ${arrayConstructor}()`);
87
+ // Transform class property without type annotation (indented, at start of line)
88
+ // Skip local variables (const/let/var)
89
+ transformed = transformed.replace(/^(\s+)(\w+)\s*=\s*\[\s*\](?=\s*[;\n])/gm, `$1$2 = new ${arrayConstructor}()`);
56
90
  return transformed;
57
91
  }
58
92
  export async function compilePhotonTS(tsFilePath, options) {
@@ -1 +1 @@
1
- {"version":3,"file":"compiler.js","sourceRoot":"","sources":["../src/compiler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compiler.js","sourceRoot":"","sources":["../src/compiler.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portel/photon-core",
3
- "version": "2.5.3",
3
+ "version": "2.5.4",
4
4
  "description": "Core library for parsing, loading, and managing .photon.ts files - runtime-agnostic foundation for building custom Photon runtimes",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
package/src/compiler.ts CHANGED
@@ -21,52 +21,94 @@ import * as crypto from 'crypto';
21
21
  * @returns Absolute path to the compiled .mjs file
22
22
  */
23
23
  /**
24
- * Transform array/map/set literals to constructor calls when using reactive imports
24
+ * Transform arrays to reactive collections for PhotonMCP classes
25
25
  *
26
- * When a file imports { Array } from '@portel/photon-core', transforms:
27
- * items: Array<Task> = []; → items: Array<Task> = new Array();
28
- * items = []; → items = new Array();
29
- * data: Map<K,V> = new Map(); → (unchanged, already correct)
26
+ * ZERO-EFFORT REACTIVITY: If a class extends PhotonMCP and has array properties,
27
+ * this transform automatically:
28
+ * 1. Injects `import { Array as ReactiveArray } from '@portel/photon-core'`
29
+ * 2. Transforms `= []` to `= new ReactiveArray()` for class properties
30
30
  *
31
- * Only transforms class properties (not local variables like const x = []).
32
- * This enables zero-effort reactivity where developers just import and use.
31
+ * Result: Developers write normal code, arrays are automatically reactive.
32
+ *
33
+ * ```typescript
34
+ * // Developer writes this (normal TypeScript):
35
+ * export default class TodoList extends PhotonMCP {
36
+ * items: Task[] = [];
37
+ * async add(text: string) { this.items.push({...}); }
38
+ * }
39
+ *
40
+ * // Compiler transforms to:
41
+ * import { Array as ReactiveArray } from '@portel/photon-core';
42
+ * export default class TodoList extends PhotonMCP {
43
+ * items = new ReactiveArray();
44
+ * async add(text: string) { this.items.push({...}); } // Auto-emits!
45
+ * }
46
+ * ```
33
47
  */
34
48
  function transformReactiveCollections(source: string): string {
35
- // Check which reactive types are imported
36
- const importMatch = source.match(
37
- /import\s*\{([^}]+)\}\s*from\s*['"]@portel\/photon-core['"]/
38
- );
39
- if (!importMatch) return source;
49
+ // Check if this is a PhotonMCP class (extends PhotonMCP)
50
+ const isPhotonMCP = /class\s+\w+\s+extends\s+PhotonMCP\b/.test(source);
51
+ if (!isPhotonMCP) return source;
40
52
 
41
- const imports = importMatch[1].split(',').map(s => s.trim());
53
+ // Check if there are array properties with = [] that need transformation
54
+ // Look for patterns like: `items: Type[] = []` or `items = []` (class properties)
55
+ const hasArrayLiterals =
56
+ /\w+\s*:\s*[^=\n]+\[\]\s*=\s*\[\s*\]/.test(source) || // typed: Type[] = []
57
+ /^\s+\w+\s*=\s*\[\s*\](?=\s*[;\n])/m.test(source); // simple: prop = []
58
+
59
+ if (!hasArrayLiterals) return source;
42
60
 
43
61
  let transformed = source;
44
62
 
45
- // Transform [] to new Array() if Array is imported
46
- if (imports.includes('Array')) {
47
- // Match class property declarations with type annotation = []
48
- // Handles: items: Array<T> = []; | items: Type[] = [];
49
- // The (?::\s*...) ensures there's a type annotation (class property pattern)
50
- transformed = transformed.replace(
51
- /(\w+)\s*:\s*(?:Array<[^>]+>|[^=\n]+\[\])\s*=\s*\[\s*\]/g,
52
- '$1 = new Array()'
63
+ // Check if Array is already imported from photon-core
64
+ const hasArrayImport = /import\s*\{[^}]*\bArray\b[^}]*\}\s*from\s*['"]@portel\/photon-core['"]/.test(source);
65
+
66
+ if (!hasArrayImport) {
67
+ // Inject ReactiveArray import (using alias to avoid shadowing issues)
68
+ // Find the photon-core import and add ReactiveArray to it, or add new import
69
+ const photonCoreImport = source.match(
70
+ /import\s*\{([^}]+)\}\s*from\s*['"]@portel\/photon-core['"]/
53
71
  );
54
72
 
55
- // Match class property without type annotation but NOT local variables
56
- // Class properties: ` items = [];` (indented, no const/let/var)
57
- // Skip: `const x = []`, `let x = []`, `var x = []`
58
- transformed = transformed.replace(
59
- /^(\s+)(\w+)\s*=\s*\[\s*\](?=\s*[;\n])/gm,
60
- (match, indent, propName) => {
61
- // Check if previous non-empty line contains const/let/var - if so, skip
62
- // This is a heuristic but works for common patterns
63
- return `${indent}${propName} = new Array()`;
73
+ if (photonCoreImport) {
74
+ // Add to existing import
75
+ const existingImports = photonCoreImport[1];
76
+ transformed = transformed.replace(
77
+ photonCoreImport[0],
78
+ `import { ${existingImports}, Array as ReactiveArray } from '@portel/photon-core'`
79
+ );
80
+ } else {
81
+ // Add new import at the top (after any existing imports)
82
+ const lastImportMatch = source.match(/^import\s+.+$/gm);
83
+ if (lastImportMatch) {
84
+ const lastImport = lastImportMatch[lastImportMatch.length - 1];
85
+ transformed = transformed.replace(
86
+ lastImport,
87
+ `${lastImport}\nimport { Array as ReactiveArray } from '@portel/photon-core';`
88
+ );
89
+ } else {
90
+ // No imports, add at top
91
+ transformed = `import { Array as ReactiveArray } from '@portel/photon-core';\n${transformed}`;
64
92
  }
65
- );
93
+ }
66
94
  }
67
95
 
68
- // Map and Set already require new, so no transform needed
69
- // (you can't write = {} for Map or Set literals)
96
+ // Determine the Array constructor name to use
97
+ const arrayConstructor = hasArrayImport ? 'Array' : 'ReactiveArray';
98
+
99
+ // Transform class property declarations with type annotation = []
100
+ // Handles: items: Task[] = []; | items: Array<T> = [];
101
+ transformed = transformed.replace(
102
+ /(\w+)\s*:\s*(?:Array<[^>]+>|[^=\n]+\[\])\s*=\s*\[\s*\]/g,
103
+ `$1 = new ${arrayConstructor}()`
104
+ );
105
+
106
+ // Transform class property without type annotation (indented, at start of line)
107
+ // Skip local variables (const/let/var)
108
+ transformed = transformed.replace(
109
+ /^(\s+)(\w+)\s*=\s*\[\s*\](?=\s*[;\n])/gm,
110
+ `$1$2 = new ${arrayConstructor}()`
111
+ );
70
112
 
71
113
  return transformed;
72
114
  }