tova 0.9.10 → 0.9.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/tova.js CHANGED
@@ -1568,7 +1568,7 @@ async function devServer(args) {
1568
1568
  for (const [dir, files] of dirGroups) {
1569
1569
  const dirName = basename(dir) === '.' ? 'app' : basename(dir);
1570
1570
  try {
1571
- const result = mergeDirectory(dir, srcDir, { strict: buildStrict, strictSecurity: buildStrictSecurity });
1571
+ const result = mergeDirectory(dir, srcDir, { strict: buildStrict, strictSecurity: buildStrictSecurity, isDev: true });
1572
1572
  if (!result) continue;
1573
1573
 
1574
1574
  const { output, single } = result;
@@ -1663,7 +1663,7 @@ async function devServer(args) {
1663
1663
 
1664
1664
  const child = spawn('bun', ['run', sf.path], {
1665
1665
  stdio: 'inherit',
1666
- env: { ...process.env, [envKey]: String(port), PORT: String(port) },
1666
+ env: { ...process.env, [envKey]: String(port), PORT: String(port), __TOVA_HMR_STATE_PATH: join(outDir, '.hmr-state.json') },
1667
1667
  });
1668
1668
 
1669
1669
  child.on('error', (err) => {
@@ -1813,7 +1813,7 @@ async function devServer(args) {
1813
1813
 
1814
1814
  for (const [dir, files] of rebuildDirGroups) {
1815
1815
  const dirName = basename(dir) === '.' ? 'app' : basename(dir);
1816
- const result = mergeDirectory(dir, srcDir, { strict: buildStrict, strictSecurity: buildStrictSecurity });
1816
+ const result = mergeDirectory(dir, srcDir, { strict: buildStrict, strictSecurity: buildStrictSecurity, isDev: true });
1817
1817
  if (!result) continue;
1818
1818
 
1819
1819
  const { output, single } = result;
@@ -1890,7 +1890,7 @@ async function devServer(args) {
1890
1890
  const port = basePort + rebuildPortOffset;
1891
1891
  const child = spawn('bun', ['run', serverPath], {
1892
1892
  stdio: 'inherit',
1893
- env: { ...process.env, PORT: String(port) },
1893
+ env: { ...process.env, PORT: String(port), __TOVA_HMR_STATE_PATH: join(outDir, '.hmr-state.json') },
1894
1894
  });
1895
1895
  processes.push({ child, label: 'server', port });
1896
1896
  rebuildPortOffset++;
@@ -1921,6 +1921,9 @@ async function devServer(args) {
1921
1921
  for (const p of processes) {
1922
1922
  try { p.child.kill('SIGKILL'); } catch {}
1923
1923
  }
1924
+ // Clean up HMR state file for fresh start next time
1925
+ const hmrPath = join(outDir, '.hmr-state.json');
1926
+ try { if (existsSync(hmrPath)) rmSync(hmrPath); } catch {}
1924
1927
  process.exit(0);
1925
1928
  });
1926
1929
 
@@ -5663,7 +5666,7 @@ function collectExports(ast, filename) {
5663
5666
  return { publicExports, allNames };
5664
5667
  }
5665
5668
 
5666
- function compileWithImports(source, filename, srcDir) {
5669
+ function compileWithImports(source, filename, srcDir, options = {}) {
5667
5670
  if (compilationCache.has(filename)) {
5668
5671
  return compilationCache.get(filename);
5669
5672
  }
@@ -5768,7 +5771,7 @@ function compileWithImports(source, filename, srcDir) {
5768
5771
  }
5769
5772
  }
5770
5773
 
5771
- const codegen = new CodeGenerator(ast, filename);
5774
+ const codegen = new CodeGenerator(ast, filename, { isDev: options.isDev });
5772
5775
  const output = codegen.generate();
5773
5776
  compilationCache.set(filename, output);
5774
5777
  return output;
@@ -5904,7 +5907,7 @@ function mergeDirectory(dir, srcDir, options = {}) {
5904
5907
  // Single file — use existing per-file compilation
5905
5908
  const file = tovaFiles[0];
5906
5909
  const source = readFileSync(file, 'utf-8');
5907
- return { output: compileWithImports(source, file, srcDir), files: [file], single: true };
5910
+ return { output: compileWithImports(source, file, srcDir, { isDev: options.isDev }), files: [file], single: true };
5908
5911
  }
5909
5912
 
5910
5913
  // Parse all files in the directory
@@ -6020,7 +6023,7 @@ function mergeDirectory(dir, srcDir, options = {}) {
6020
6023
  }
6021
6024
 
6022
6025
  // Run codegen on merged AST
6023
- const codegen = new CodeGenerator(mergedAST, dir);
6026
+ const codegen = new CodeGenerator(mergedAST, dir, { isDev: options.isDev });
6024
6027
  const output = codegen.generate();
6025
6028
 
6026
6029
  // Collect source content from all files for source maps
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tova",
3
- "version": "0.9.10",
3
+ "version": "0.9.11",
4
4
  "description": "Tova — a modern programming language that transpiles to JavaScript, unifying frontend and backend",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -59,6 +59,7 @@ export class CodeGenerator {
59
59
  this.ast = ast;
60
60
  this.filename = filename;
61
61
  this._sourceMaps = options.sourceMaps !== false; // default true; pass false for REPL/check
62
+ this._isDev = options.isDev || false;
62
63
  }
63
64
 
64
65
  // Group blocks by name (null name = "default")
@@ -250,6 +251,7 @@ export class CodeGenerator {
250
251
  for (const [name, blocks] of serverGroups) {
251
252
  const gen = new (getServerCodegen())();
252
253
  gen._sourceMapsEnabled = this._sourceMaps;
254
+ gen._isDev = this._isDev;
253
255
  const key = name || 'default';
254
256
  // Build peer blocks map (all named blocks except self)
255
257
  let peerBlocks = null;
@@ -240,6 +240,7 @@ export class ServerCodegen extends BaseCodegen {
240
240
  const functions = [];
241
241
  const middlewares = [];
242
242
  const otherStatements = [];
243
+ const hmrVarNames = [];
243
244
  let healthPath = null;
244
245
  let healthChecks = null;
245
246
  let corsConfig = null;
@@ -2142,8 +2143,29 @@ export class ServerCodegen extends BaseCodegen {
2142
2143
  // ════════════════════════════════════════════════════════════
2143
2144
  // 13. Other statements + Server Functions
2144
2145
  // ════════════════════════════════════════════════════════════
2146
+ if (this._isDev) {
2147
+ lines.push('// ── HMR State ──');
2148
+ lines.push('const __hmrStatePath = process.env.__TOVA_HMR_STATE_PATH || "";');
2149
+ lines.push('let __hmrState = {};');
2150
+ lines.push('if (__hmrStatePath) { try { __hmrState = JSON.parse(require("fs").readFileSync(__hmrStatePath, "utf-8")); } catch {} }');
2151
+ lines.push('');
2152
+ }
2145
2153
  for (const stmt of otherStatements) {
2146
- lines.push(this.generateStatement(stmt));
2154
+ // HMR wrapping: new variable declarations get restored from __hmrState
2155
+ const isHmrEligible = this._isDev && stmt.targets && stmt.targets.length === 1 &&
2156
+ stmt.values && stmt.values.length === 1 && typeof stmt.targets[0] === 'string' &&
2157
+ (stmt.type === 'Assignment' ? !this.isDeclared(stmt.targets[0]) : stmt.type === 'VarDeclaration');
2158
+ if (isHmrEligible) {
2159
+ const name = stmt.targets[0];
2160
+ hmrVarNames.push(name);
2161
+ this.declareVar(name);
2162
+ if (stmt.isPublic) this._userDefinedNames.add(name);
2163
+ const initExpr = this.genExpression(stmt.values[0]);
2164
+ const keyword = stmt.type === 'VarDeclaration' ? 'let' : 'const';
2165
+ lines.push(`${keyword} ${name} = (${JSON.stringify(name)} in __hmrState) ? __hmrState[${JSON.stringify(name)}] : ${initExpr};`);
2166
+ } else {
2167
+ lines.push(this.generateStatement(stmt));
2168
+ }
2147
2169
  }
2148
2170
 
2149
2171
  if (functions.length > 0) {
@@ -3688,10 +3710,24 @@ export class ServerCodegen extends BaseCodegen {
3688
3710
  // 24. Graceful Shutdown — on_stop hooks (F3) + clearInterval (F8)
3689
3711
  // ════════════════════════════════════════════════════════════
3690
3712
  lines.push('// ── Graceful Shutdown ──');
3713
+ // HMR state-save helper (emitted as first action in __shutdown)
3714
+ const hmrSaveLines = [];
3715
+ if (this._isDev && hmrVarNames.length > 0) {
3716
+ const entries = hmrVarNames.map(n => `${JSON.stringify(n)}: ${n}`).join(', ');
3717
+ hmrSaveLines.push(` try { require("fs").writeFileSync(__hmrStatePath, JSON.stringify({ ${entries} })); } catch {}`);
3718
+ }
3691
3719
  if (isFastMode) {
3692
- lines.push('function __shutdown() { __server.stop(); process.exit(0); }');
3720
+ if (hmrSaveLines.length > 0) {
3721
+ lines.push('function __shutdown() {');
3722
+ for (const l of hmrSaveLines) lines.push(l);
3723
+ lines.push(' __server.stop(); process.exit(0);');
3724
+ lines.push('}');
3725
+ } else {
3726
+ lines.push('function __shutdown() { __server.stop(); process.exit(0); }');
3727
+ }
3693
3728
  } else {
3694
3729
  lines.push('async function __shutdown() {');
3730
+ for (const l of hmrSaveLines) lines.push(l);
3695
3731
  lines.push(` console.log(\`Tova server${label} shutting down...\`);`);
3696
3732
  lines.push(' __shuttingDown = true;');
3697
3733
  lines.push(' __server.stop();');
package/src/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Auto-generated by scripts/embed-runtime.js — do not edit
2
- export const VERSION = "0.9.10";
2
+ export const VERSION = "0.9.11";