tova 0.5.0 → 0.7.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.
- package/bin/tova.js +109 -57
- package/package.json +7 -2
- package/src/analyzer/analyzer.js +315 -79
- package/src/analyzer/{client-analyzer.js → browser-analyzer.js} +20 -17
- package/src/analyzer/form-analyzer.js +113 -0
- package/src/analyzer/scope.js +2 -2
- package/src/codegen/base-codegen.js +1 -0
- package/src/codegen/{client-codegen.js → browser-codegen.js} +444 -5
- package/src/codegen/cli-codegen.js +386 -0
- package/src/codegen/codegen.js +163 -45
- package/src/codegen/edge-codegen.js +1351 -0
- package/src/codegen/form-codegen.js +553 -0
- package/src/codegen/security-codegen.js +5 -5
- package/src/codegen/server-codegen.js +88 -7
- package/src/diagnostics/error-codes.js +1 -1
- package/src/docs/generator.js +1 -1
- package/src/formatter/formatter.js +4 -4
- package/src/lexer/tokens.js +12 -2
- package/src/lsp/server.js +1 -1
- package/src/parser/ast.js +45 -5
- package/src/parser/{client-ast.js → browser-ast.js} +3 -3
- package/src/parser/{client-parser.js → browser-parser.js} +42 -15
- package/src/parser/cli-ast.js +35 -0
- package/src/parser/cli-parser.js +140 -0
- package/src/parser/edge-ast.js +83 -0
- package/src/parser/edge-parser.js +262 -0
- package/src/parser/form-ast.js +80 -0
- package/src/parser/form-parser.js +206 -0
- package/src/parser/parser.js +86 -53
- package/src/registry/block-registry.js +56 -0
- package/src/registry/plugins/bench-plugin.js +23 -0
- package/src/registry/plugins/browser-plugin.js +30 -0
- package/src/registry/plugins/cli-plugin.js +24 -0
- package/src/registry/plugins/data-plugin.js +21 -0
- package/src/registry/plugins/edge-plugin.js +32 -0
- package/src/registry/plugins/security-plugin.js +27 -0
- package/src/registry/plugins/server-plugin.js +46 -0
- package/src/registry/plugins/shared-plugin.js +17 -0
- package/src/registry/plugins/test-plugin.js +23 -0
- package/src/registry/register-all.js +25 -0
- package/src/runtime/ssr.js +2 -2
- package/src/stdlib/inline.js +178 -3
- package/src/version.js +1 -1
package/bin/tova.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { resolve, basename, dirname, join, relative } from 'path';
|
|
4
4
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSync, copyFileSync, rmSync, chmodSync, renameSync, watch as fsWatch } from 'fs';
|
|
5
5
|
import { spawn } from 'child_process';
|
|
6
|
-
|
|
6
|
+
import { createHash as _cryptoHash } from 'crypto';
|
|
7
7
|
import { Lexer } from '../src/lexer/lexer.js';
|
|
8
8
|
import { Parser } from '../src/parser/parser.js';
|
|
9
9
|
import { Analyzer } from '../src/analyzer/analyzer.js';
|
|
@@ -36,9 +36,9 @@ const color = {
|
|
|
36
36
|
};
|
|
37
37
|
|
|
38
38
|
const HELP = `
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
╔╦╗╔═╗╦ ╦╔═╗
|
|
40
|
+
║ ║ ║╚╗╔╝╠═╣
|
|
41
|
+
╩ ╚═╝ ╚╝ ╩ ╩ v${VERSION}
|
|
42
42
|
|
|
43
43
|
Created by Enoch Kujem Abassey
|
|
44
44
|
A modern full-stack language that transpiles to JavaScript
|
|
@@ -601,6 +601,18 @@ async function runFile(filePath, options = {}) {
|
|
|
601
601
|
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
|
|
602
602
|
const stdlib = getRunStdlib();
|
|
603
603
|
|
|
604
|
+
// CLI mode: execute the cli code directly
|
|
605
|
+
if (output.isCli) {
|
|
606
|
+
let code = stdlib + '\n' + output.cli;
|
|
607
|
+
code = code.replace(/^export /gm, '');
|
|
608
|
+
// Override process.argv for cli dispatch
|
|
609
|
+
const scriptArgs = options.scriptArgs || [];
|
|
610
|
+
code = `process.argv = ["node", ${JSON.stringify(resolved)}, ...${JSON.stringify(scriptArgs)}];\n` + code;
|
|
611
|
+
const fn = new AsyncFunction('__tova_args', '__tova_filename', '__tova_dirname', code);
|
|
612
|
+
await fn(scriptArgs, resolved, dirname(resolved));
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
|
|
604
616
|
// Compile .tova dependencies and inline them
|
|
605
617
|
let depCode = '';
|
|
606
618
|
if (hasTovaImports) {
|
|
@@ -616,7 +628,7 @@ async function runFile(filePath, options = {}) {
|
|
|
616
628
|
}
|
|
617
629
|
}
|
|
618
630
|
|
|
619
|
-
let code = stdlib + '\n' + depCode + (output.shared || '') + '\n' + (output.server || output.
|
|
631
|
+
let code = stdlib + '\n' + depCode + (output.shared || '') + '\n' + (output.server || output.browser || '');
|
|
620
632
|
// Strip 'export ' keywords — not valid inside AsyncFunction (used in tova build only)
|
|
621
633
|
code = code.replace(/^export /gm, '');
|
|
622
634
|
// Strip import lines for local modules (already inlined above)
|
|
@@ -766,8 +778,29 @@ async function buildProject(args) {
|
|
|
766
778
|
const outSubDir = dirname(join(outDir, outBaseName));
|
|
767
779
|
if (outSubDir !== outDir) mkdirSync(outSubDir, { recursive: true });
|
|
768
780
|
|
|
781
|
+
// CLI files: write single executable <name>.js with shebang
|
|
782
|
+
if (output.isCli) {
|
|
783
|
+
if (output.cli && output.cli.trim()) {
|
|
784
|
+
const cliPath = join(outDir, `${outBaseName}.js`);
|
|
785
|
+
const shebang = '#!/usr/bin/env node\n';
|
|
786
|
+
writeFileSync(cliPath, shebang + output.cli);
|
|
787
|
+
try { chmodSync(cliPath, 0o755); } catch (e) { /* ignore on Windows */ }
|
|
788
|
+
if (!isQuiet) console.log(` ✓ ${relLabel} → ${relative('.', cliPath)} [cli]${timing}`);
|
|
789
|
+
}
|
|
790
|
+
if (!noCache) {
|
|
791
|
+
const outputPaths = {};
|
|
792
|
+
if (output.cli && output.cli.trim()) outputPaths.cli = join(outDir, `${outBaseName}.js`);
|
|
793
|
+
if (single) {
|
|
794
|
+
const absFile = files[0];
|
|
795
|
+
const sourceContent = readFileSync(absFile, 'utf-8');
|
|
796
|
+
buildCache.set(absFile, sourceContent, outputPaths);
|
|
797
|
+
} else {
|
|
798
|
+
buildCache.setGroup(`dir:${dir}`, files, outputPaths);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
769
802
|
// Module files: write single <name>.js (not .shared.js)
|
|
770
|
-
if (output.isModule) {
|
|
803
|
+
else if (output.isModule) {
|
|
771
804
|
if (output.shared && output.shared.trim()) {
|
|
772
805
|
const modulePath = join(outDir, `${outBaseName}.js`);
|
|
773
806
|
writeFileSync(modulePath, generateSourceMap(output.shared, modulePath));
|
|
@@ -800,11 +833,18 @@ async function buildProject(args) {
|
|
|
800
833
|
if (!isQuiet) console.log(` ✓ ${relLabel} → ${relative('.', serverPath)}${timing}`);
|
|
801
834
|
}
|
|
802
835
|
|
|
803
|
-
// Write default
|
|
804
|
-
if (output.
|
|
805
|
-
const
|
|
806
|
-
writeFileSync(
|
|
807
|
-
if (!isQuiet) console.log(` ✓ ${relLabel} → ${relative('.',
|
|
836
|
+
// Write default browser
|
|
837
|
+
if (output.browser) {
|
|
838
|
+
const browserPath = join(outDir, `${outBaseName}.browser.js`);
|
|
839
|
+
writeFileSync(browserPath, generateSourceMap(output.browser, browserPath));
|
|
840
|
+
if (!isQuiet) console.log(` ✓ ${relLabel} → ${relative('.', browserPath)}${timing}`);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// Write default edge
|
|
844
|
+
if (output.edge) {
|
|
845
|
+
const edgePath = join(outDir, `${outBaseName}.edge.js`);
|
|
846
|
+
writeFileSync(edgePath, generateSourceMap(output.edge, edgePath));
|
|
847
|
+
if (!isQuiet) console.log(` ✓ ${relLabel} → ${relative('.', edgePath)} [edge]${timing}`);
|
|
808
848
|
}
|
|
809
849
|
|
|
810
850
|
// Write named server blocks (multi-block)
|
|
@@ -817,13 +857,23 @@ async function buildProject(args) {
|
|
|
817
857
|
}
|
|
818
858
|
}
|
|
819
859
|
|
|
820
|
-
// Write named
|
|
821
|
-
if (output.multiBlock && output.
|
|
822
|
-
for (const [name, code] of Object.entries(output.
|
|
860
|
+
// Write named edge blocks (multi-block)
|
|
861
|
+
if (output.multiBlock && output.edges) {
|
|
862
|
+
for (const [name, code] of Object.entries(output.edges)) {
|
|
863
|
+
if (name === 'default') continue;
|
|
864
|
+
const path = join(outDir, `${outBaseName}.edge.${name}.js`);
|
|
865
|
+
writeFileSync(path, code);
|
|
866
|
+
if (!isQuiet) console.log(` ✓ ${relLabel} → ${relative('.', path)} [edge:${name}]${timing}`);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// Write named browser blocks (multi-block)
|
|
871
|
+
if (output.multiBlock && output.browsers) {
|
|
872
|
+
for (const [name, code] of Object.entries(output.browsers)) {
|
|
823
873
|
if (name === 'default') continue;
|
|
824
|
-
const path = join(outDir, `${outBaseName}.
|
|
874
|
+
const path = join(outDir, `${outBaseName}.browser.${name}.js`);
|
|
825
875
|
writeFileSync(path, code);
|
|
826
|
-
if (!isQuiet) console.log(` ✓ ${relLabel} → ${relative('.', path)} [
|
|
876
|
+
if (!isQuiet) console.log(` ✓ ${relLabel} → ${relative('.', path)} [browser:${name}]${timing}`);
|
|
827
877
|
}
|
|
828
878
|
}
|
|
829
879
|
|
|
@@ -832,7 +882,7 @@ async function buildProject(args) {
|
|
|
832
882
|
const outputPaths = {};
|
|
833
883
|
if (output.shared && output.shared.trim()) outputPaths.shared = join(outDir, `${outBaseName}.shared.js`);
|
|
834
884
|
if (output.server) outputPaths.server = join(outDir, `${outBaseName}.server.js`);
|
|
835
|
-
if (output.
|
|
885
|
+
if (output.browser) outputPaths.browser = join(outDir, `${outBaseName}.browser.js`);
|
|
836
886
|
if (single) {
|
|
837
887
|
const absFile = files[0];
|
|
838
888
|
const sourceContent = readFileSync(absFile, 'utf-8');
|
|
@@ -1083,10 +1133,10 @@ async function devServer(args) {
|
|
|
1083
1133
|
writeFileSync(join(outDir, `${outBaseName}.shared.js`), output.shared);
|
|
1084
1134
|
}
|
|
1085
1135
|
|
|
1086
|
-
if (output.
|
|
1087
|
-
const p = join(outDir, `${outBaseName}.
|
|
1088
|
-
writeFileSync(p, output.
|
|
1089
|
-
clientHTML = await generateDevHTML(output.
|
|
1136
|
+
if (output.browser) {
|
|
1137
|
+
const p = join(outDir, `${outBaseName}.browser.js`);
|
|
1138
|
+
writeFileSync(p, output.browser);
|
|
1139
|
+
clientHTML = await generateDevHTML(output.browser, srcDir, actualReloadPort);
|
|
1090
1140
|
writeFileSync(join(outDir, 'index.html'), clientHTML);
|
|
1091
1141
|
hasClient = true;
|
|
1092
1142
|
}
|
|
@@ -1117,10 +1167,10 @@ async function devServer(args) {
|
|
|
1117
1167
|
}
|
|
1118
1168
|
}
|
|
1119
1169
|
|
|
1120
|
-
if (output.multiBlock && output.
|
|
1121
|
-
for (const [name, code] of Object.entries(output.
|
|
1170
|
+
if (output.multiBlock && output.browsers) {
|
|
1171
|
+
for (const [name, code] of Object.entries(output.browsers)) {
|
|
1122
1172
|
if (name === 'default') continue;
|
|
1123
|
-
const p = join(outDir, `${outBaseName}.
|
|
1173
|
+
const p = join(outDir, `${outBaseName}.browser.${name}.js`);
|
|
1124
1174
|
writeFileSync(p, code);
|
|
1125
1175
|
}
|
|
1126
1176
|
}
|
|
@@ -1231,9 +1281,9 @@ async function devServer(args) {
|
|
|
1231
1281
|
if (output.shared && output.shared.trim()) {
|
|
1232
1282
|
writeFileSync(join(outDir, `${outBaseName}.shared.js`), output.shared);
|
|
1233
1283
|
}
|
|
1234
|
-
if (output.
|
|
1235
|
-
writeFileSync(join(outDir, `${outBaseName}.
|
|
1236
|
-
rebuildClientHTML = await generateDevHTML(output.
|
|
1284
|
+
if (output.browser) {
|
|
1285
|
+
writeFileSync(join(outDir, `${outBaseName}.browser.js`), output.browser);
|
|
1286
|
+
rebuildClientHTML = await generateDevHTML(output.browser, srcDir, actualReloadPort);
|
|
1237
1287
|
writeFileSync(join(outDir, 'index.html'), rebuildClientHTML);
|
|
1238
1288
|
}
|
|
1239
1289
|
if (output.server) {
|
|
@@ -2507,7 +2557,7 @@ async function startRepl() {
|
|
|
2507
2557
|
'in', 'return', 'match', 'type', 'import', 'from', 'and', 'or', 'not',
|
|
2508
2558
|
'try', 'catch', 'finally', 'break', 'continue', 'async', 'await',
|
|
2509
2559
|
'guard', 'interface', 'derive', 'pub', 'impl', 'trait', 'defer',
|
|
2510
|
-
'yield', 'extern', 'is', 'with', 'as', 'export', 'server', 'client', 'shared',
|
|
2560
|
+
'yield', 'extern', 'is', 'with', 'as', 'export', 'server', 'client', 'browser', 'shared',
|
|
2511
2561
|
]);
|
|
2512
2562
|
|
|
2513
2563
|
const TYPE_NAMES = new Set([
|
|
@@ -3007,7 +3057,7 @@ async function binaryBuild(srcDir, outputName, outDir) {
|
|
|
3007
3057
|
// Step 1: Compile all .tova files to JS
|
|
3008
3058
|
const sharedParts = [];
|
|
3009
3059
|
const serverParts = [];
|
|
3010
|
-
const
|
|
3060
|
+
const browserParts = [];
|
|
3011
3061
|
|
|
3012
3062
|
for (const file of tovaFiles) {
|
|
3013
3063
|
try {
|
|
@@ -3015,7 +3065,7 @@ async function binaryBuild(srcDir, outputName, outDir) {
|
|
|
3015
3065
|
const output = compileTova(source, file);
|
|
3016
3066
|
if (output.shared) sharedParts.push(output.shared);
|
|
3017
3067
|
if (output.server) serverParts.push(output.server);
|
|
3018
|
-
if (output.
|
|
3068
|
+
if (output.browser) browserParts.push(output.browser);
|
|
3019
3069
|
} catch (err) {
|
|
3020
3070
|
console.error(` Error in ${relative(srcDir, file)}: ${err.message}`);
|
|
3021
3071
|
process.exit(1);
|
|
@@ -3085,7 +3135,7 @@ async function productionBuild(srcDir, outDir) {
|
|
|
3085
3135
|
|
|
3086
3136
|
console.log(`\n Production build...\n`);
|
|
3087
3137
|
|
|
3088
|
-
const
|
|
3138
|
+
const browserParts = [];
|
|
3089
3139
|
const serverParts = [];
|
|
3090
3140
|
const sharedParts = [];
|
|
3091
3141
|
let cssContent = '';
|
|
@@ -3097,14 +3147,14 @@ async function productionBuild(srcDir, outDir) {
|
|
|
3097
3147
|
|
|
3098
3148
|
if (output.shared) sharedParts.push(output.shared);
|
|
3099
3149
|
if (output.server) serverParts.push(output.server);
|
|
3100
|
-
if (output.
|
|
3150
|
+
if (output.browser) browserParts.push(output.browser);
|
|
3101
3151
|
} catch (err) {
|
|
3102
3152
|
console.error(` Error in ${relative(srcDir, file)}: ${err.message}`);
|
|
3103
3153
|
process.exit(1);
|
|
3104
3154
|
}
|
|
3105
3155
|
}
|
|
3106
3156
|
|
|
3107
|
-
const allClientCode =
|
|
3157
|
+
const allClientCode = browserParts.join('\n');
|
|
3108
3158
|
const allServerCode = serverParts.join('\n');
|
|
3109
3159
|
const allSharedCode = sharedParts.join('\n');
|
|
3110
3160
|
|
|
@@ -3452,7 +3502,8 @@ class BuildCache {
|
|
|
3452
3502
|
}
|
|
3453
3503
|
|
|
3454
3504
|
_hashContent(content) {
|
|
3455
|
-
return Bun.hash(content).toString(16);
|
|
3505
|
+
if (typeof Bun !== 'undefined' && Bun.hash) return Bun.hash(content).toString(16);
|
|
3506
|
+
return _cryptoHash('md5').update(content).digest('hex');
|
|
3456
3507
|
}
|
|
3457
3508
|
|
|
3458
3509
|
load() {
|
|
@@ -3493,7 +3544,8 @@ class BuildCache {
|
|
|
3493
3544
|
for (const f of files.slice().sort()) {
|
|
3494
3545
|
combined += f + readFileSync(f, 'utf-8');
|
|
3495
3546
|
}
|
|
3496
|
-
return Bun.hash(combined).toString(16);
|
|
3547
|
+
if (typeof Bun !== 'undefined' && Bun.hash) return Bun.hash(combined).toString(16);
|
|
3548
|
+
return _cryptoHash('md5').update(combined).digest('hex');
|
|
3497
3549
|
}
|
|
3498
3550
|
|
|
3499
3551
|
// Store compiled output for a multi-file group
|
|
@@ -3565,7 +3617,7 @@ function getCompiledExtension(tovaPath) {
|
|
|
3565
3617
|
const lexer = new Lexer(src, tovaPath);
|
|
3566
3618
|
const tokens = lexer.tokenize();
|
|
3567
3619
|
// Check if any top-level token is a block keyword (shared/server/client/test/bench/data)
|
|
3568
|
-
const BLOCK_KEYWORDS = new Set(['shared', 'server', 'client', 'test', 'bench', 'data']);
|
|
3620
|
+
const BLOCK_KEYWORDS = new Set(['shared', 'server', 'client', 'browser', 'test', 'bench', 'data']);
|
|
3569
3621
|
let depth = 0;
|
|
3570
3622
|
for (const tok of tokens) {
|
|
3571
3623
|
if (tok.type === 'LBRACE') depth++;
|
|
@@ -3692,7 +3744,7 @@ function collectExports(ast, filename) {
|
|
|
3692
3744
|
|
|
3693
3745
|
for (const node of ast.body) {
|
|
3694
3746
|
// Also collect exports from inside shared/server/client blocks
|
|
3695
|
-
if (node.type === 'SharedBlock' || node.type === 'ServerBlock' || node.type === '
|
|
3747
|
+
if (node.type === 'SharedBlock' || node.type === 'ServerBlock' || node.type === 'BrowserBlock') {
|
|
3696
3748
|
if (node.body) {
|
|
3697
3749
|
for (const inner of node.body) {
|
|
3698
3750
|
collectFromNode(inner);
|
|
@@ -3722,7 +3774,7 @@ function compileWithImports(source, filename, srcDir) {
|
|
|
3722
3774
|
const ast = parser.parse();
|
|
3723
3775
|
|
|
3724
3776
|
// Cache module type from AST (avoids regex heuristic on subsequent lookups)
|
|
3725
|
-
const hasBlocks = ast.body.some(n => n.type === 'SharedBlock' || n.type === 'ServerBlock' || n.type === '
|
|
3777
|
+
const hasBlocks = ast.body.some(n => n.type === 'SharedBlock' || n.type === 'ServerBlock' || n.type === 'BrowserBlock' || n.type === 'TestBlock' || n.type === 'BenchBlock' || n.type === 'DataBlock');
|
|
3726
3778
|
moduleTypeCache.set(filename, hasBlocks ? '.shared.js' : '.js');
|
|
3727
3779
|
|
|
3728
3780
|
// Collect this module's exports for validation
|
|
@@ -3820,32 +3872,32 @@ function validateMergedAST(mergedBlocks, sourceFiles) {
|
|
|
3820
3872
|
);
|
|
3821
3873
|
}
|
|
3822
3874
|
|
|
3823
|
-
// Check
|
|
3824
|
-
const
|
|
3825
|
-
for (const block of mergedBlocks.
|
|
3875
|
+
// Check browser blocks — top-level declarations only
|
|
3876
|
+
const browserDecls = { component: new Map(), state: new Map(), computed: new Map(), store: new Map(), fn: new Map() };
|
|
3877
|
+
for (const block of mergedBlocks.browserBlocks) {
|
|
3826
3878
|
for (const stmt of block.body) {
|
|
3827
3879
|
const loc = stmt.loc || block.loc;
|
|
3828
3880
|
if (stmt.type === 'ComponentDeclaration') {
|
|
3829
|
-
if (
|
|
3830
|
-
else
|
|
3881
|
+
if (browserDecls.component.has(stmt.name)) addDup('component', stmt.name, browserDecls.component.get(stmt.name), loc);
|
|
3882
|
+
else browserDecls.component.set(stmt.name, loc);
|
|
3831
3883
|
} else if (stmt.type === 'StateDeclaration') {
|
|
3832
3884
|
const name = stmt.name || (stmt.targets && stmt.targets[0]);
|
|
3833
3885
|
if (name) {
|
|
3834
|
-
if (
|
|
3835
|
-
else
|
|
3886
|
+
if (browserDecls.state.has(name)) addDup('state', name, browserDecls.state.get(name), loc);
|
|
3887
|
+
else browserDecls.state.set(name, loc);
|
|
3836
3888
|
}
|
|
3837
3889
|
} else if (stmt.type === 'ComputedDeclaration') {
|
|
3838
3890
|
const name = stmt.name;
|
|
3839
3891
|
if (name) {
|
|
3840
|
-
if (
|
|
3841
|
-
else
|
|
3892
|
+
if (browserDecls.computed.has(name)) addDup('computed', name, browserDecls.computed.get(name), loc);
|
|
3893
|
+
else browserDecls.computed.set(name, loc);
|
|
3842
3894
|
}
|
|
3843
3895
|
} else if (stmt.type === 'StoreDeclaration') {
|
|
3844
|
-
if (
|
|
3845
|
-
else
|
|
3896
|
+
if (browserDecls.store.has(stmt.name)) addDup('store', stmt.name, browserDecls.store.get(stmt.name), loc);
|
|
3897
|
+
else browserDecls.store.set(stmt.name, loc);
|
|
3846
3898
|
} else if (stmt.type === 'FunctionDeclaration') {
|
|
3847
|
-
if (
|
|
3848
|
-
else
|
|
3899
|
+
if (browserDecls.fn.has(stmt.name)) addDup('function', stmt.name, browserDecls.fn.get(stmt.name), loc);
|
|
3900
|
+
else browserDecls.fn.set(stmt.name, loc);
|
|
3849
3901
|
}
|
|
3850
3902
|
}
|
|
3851
3903
|
}
|
|
@@ -3989,7 +4041,7 @@ function mergeDirectory(dir, srcDir, options = {}) {
|
|
|
3989
4041
|
const mergedBody = [];
|
|
3990
4042
|
const sharedBlocks = [];
|
|
3991
4043
|
const serverBlocks = [];
|
|
3992
|
-
const
|
|
4044
|
+
const browserBlocks = [];
|
|
3993
4045
|
|
|
3994
4046
|
for (const { file, ast } of parsedFiles) {
|
|
3995
4047
|
for (const node of ast.body) {
|
|
@@ -4010,14 +4062,14 @@ function mergeDirectory(dir, srcDir, options = {}) {
|
|
|
4010
4062
|
|
|
4011
4063
|
if (node.type === 'SharedBlock') sharedBlocks.push(node);
|
|
4012
4064
|
else if (node.type === 'ServerBlock') serverBlocks.push(node);
|
|
4013
|
-
else if (node.type === '
|
|
4065
|
+
else if (node.type === 'BrowserBlock') browserBlocks.push(node);
|
|
4014
4066
|
|
|
4015
4067
|
mergedBody.push(node);
|
|
4016
4068
|
}
|
|
4017
4069
|
}
|
|
4018
4070
|
|
|
4019
4071
|
// Validate for duplicate declarations across files
|
|
4020
|
-
validateMergedAST({ sharedBlocks, serverBlocks,
|
|
4072
|
+
validateMergedAST({ sharedBlocks, serverBlocks, browserBlocks }, tovaFiles);
|
|
4021
4073
|
|
|
4022
4074
|
// Build merged Program AST
|
|
4023
4075
|
const mergedAST = new Program(mergedBody);
|
|
@@ -4655,9 +4707,9 @@ async function infoCommand() {
|
|
|
4655
4707
|
const config = resolveConfig(process.cwd());
|
|
4656
4708
|
const hasTOML = config._source === 'tova.toml';
|
|
4657
4709
|
|
|
4658
|
-
console.log(`\n
|
|
4659
|
-
console.log(`
|
|
4660
|
-
console.log(`
|
|
4710
|
+
console.log(`\n ╔╦╗╔═╗╦ ╦╔═╗`);
|
|
4711
|
+
console.log(` ║ ║ ║╚╗╔╝╠═╣`);
|
|
4712
|
+
console.log(` ╩ ╚═╝ ╚╝ ╩ ╩ v${VERSION}\n`);
|
|
4661
4713
|
|
|
4662
4714
|
// Bun version
|
|
4663
4715
|
let bunVersion = 'not found';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tova",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
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",
|
|
@@ -36,7 +36,12 @@
|
|
|
36
36
|
"url": "https://github.com/tova-lang/tova-lang/issues"
|
|
37
37
|
},
|
|
38
38
|
"author": "Enoch Kujem Abassey",
|
|
39
|
-
"keywords": [
|
|
39
|
+
"keywords": [
|
|
40
|
+
"language",
|
|
41
|
+
"transpiler",
|
|
42
|
+
"fullstack",
|
|
43
|
+
"javascript"
|
|
44
|
+
],
|
|
40
45
|
"license": "MIT",
|
|
41
46
|
"devDependencies": {
|
|
42
47
|
"@codemirror/autocomplete": "^6.20.0",
|