tova 0.11.21 → 0.11.22
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/package.json +1 -1
- package/src/cli/run.js +53 -37
- package/src/version.js +1 -1
package/package.json
CHANGED
package/src/cli/run.js
CHANGED
|
@@ -1,11 +1,54 @@
|
|
|
1
1
|
import { resolve, dirname, join } from 'path';
|
|
2
2
|
import { readFileSync, existsSync } from 'fs';
|
|
3
3
|
import { createRequire as _createRequire } from 'module';
|
|
4
|
+
import { pathToFileURL } from 'url';
|
|
4
5
|
import { compileTova } from './compile.js';
|
|
5
6
|
import { getRunStdlib } from './utils.js';
|
|
6
7
|
import { resolveConfig } from '../config/resolve.js';
|
|
7
8
|
import { richError } from '../diagnostics/formatter.js';
|
|
8
9
|
|
|
10
|
+
function findLocalTovaImports(source, fromFile) {
|
|
11
|
+
const importDetectRegex = /import\s+(?:\{[^}]*\}|[\w$]+|\*\s+as\s+[\w$]+)\s+from\s+['"]([^'"]+)['"]/gm;
|
|
12
|
+
let importMatch;
|
|
13
|
+
const tovaImportPaths = [];
|
|
14
|
+
while ((importMatch = importDetectRegex.exec(source)) !== null) {
|
|
15
|
+
const importSource = importMatch[1];
|
|
16
|
+
if (!importSource.startsWith('.') && !importSource.startsWith('/')) continue;
|
|
17
|
+
let depPath = resolve(dirname(fromFile), importSource);
|
|
18
|
+
if (!depPath.endsWith('.tova') && existsSync(depPath + '.tova')) {
|
|
19
|
+
depPath = depPath + '.tova';
|
|
20
|
+
}
|
|
21
|
+
if (depPath.endsWith('.tova') && existsSync(depPath)) {
|
|
22
|
+
tovaImportPaths.push({ source: importSource, resolved: depPath });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return tovaImportPaths;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function stripInlinedImports(code, importSources) {
|
|
29
|
+
let stripped = code;
|
|
30
|
+
for (const importSource of importSources) {
|
|
31
|
+
const escaped = importSource.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
32
|
+
stripped = stripped.replace(new RegExp('^import\\s+(?:\\{[^}]*\\}|[\\w$]+|\\*\\s+as\\s+[\\w$]+)\\s+from\\s+[\'\"]' + escaped + '[\'\"];?\\s*$', 'gm'), '');
|
|
33
|
+
}
|
|
34
|
+
return stripped;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function rewriteImportsForRuntime(code, baseFileUrl) {
|
|
38
|
+
return code.replace(/^import\s+(\{[^}]*\}|[\w$]+|\*\s+as\s+[\w$]+)\s+from\s+['"]([^'"]+)['"];?\s*$/gm, (match, clause, source) => {
|
|
39
|
+
const importExpr = source.startsWith('.') || source.startsWith('/')
|
|
40
|
+
? `await import(new URL(${JSON.stringify(source)}, ${JSON.stringify(baseFileUrl)}).href)`
|
|
41
|
+
: `await import(${JSON.stringify(source)})`;
|
|
42
|
+
if (clause.startsWith('{')) {
|
|
43
|
+
return `const ${clause.replace(/\bas\b/g, ':')} = ${importExpr};`;
|
|
44
|
+
}
|
|
45
|
+
if (clause.startsWith('*')) {
|
|
46
|
+
return `const ${clause.replace(/^\*\s+as\s+/, '').trim()} = ${importExpr};`;
|
|
47
|
+
}
|
|
48
|
+
return `const { default: ${clause.trim()} } = ${importExpr};`;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
9
52
|
export async function runFile(filePath, options = {}) {
|
|
10
53
|
if (!filePath) {
|
|
11
54
|
// If tova.toml exists, try to find a main file in the entry directory
|
|
@@ -34,26 +77,13 @@ export async function runFile(filePath, options = {}) {
|
|
|
34
77
|
}
|
|
35
78
|
|
|
36
79
|
const source = readFileSync(resolved, 'utf-8');
|
|
80
|
+
const fileUrl = pathToFileURL(resolved).href;
|
|
37
81
|
|
|
38
82
|
try {
|
|
39
|
-
|
|
40
|
-
const importDetectRegex = /import\s+(?:\{[^}]*\}|[\w$]+|\*\s+as\s+[\w$]+)\s+from\s+['"]([^'"]+)['"]/gm;
|
|
41
|
-
let importMatch;
|
|
42
|
-
const tovaImportPaths = [];
|
|
43
|
-
while ((importMatch = importDetectRegex.exec(source)) !== null) {
|
|
44
|
-
const importSource = importMatch[1];
|
|
45
|
-
if (!importSource.startsWith('.') && !importSource.startsWith('/')) continue;
|
|
46
|
-
let depPath = resolve(dirname(resolved), importSource);
|
|
47
|
-
if (!depPath.endsWith('.tova') && existsSync(depPath + '.tova')) {
|
|
48
|
-
depPath = depPath + '.tova';
|
|
49
|
-
}
|
|
50
|
-
if (depPath.endsWith('.tova') && existsSync(depPath)) {
|
|
51
|
-
tovaImportPaths.push({ source: importSource, resolved: depPath });
|
|
52
|
-
}
|
|
53
|
-
}
|
|
83
|
+
const tovaImportPaths = findLocalTovaImports(source, resolved);
|
|
54
84
|
const hasTovaImports = tovaImportPaths.length > 0;
|
|
55
85
|
|
|
56
|
-
const output = compileTova(source,
|
|
86
|
+
const output = compileTova(source, resolved, { strict: options.strict, strictSecurity: options.strictSecurity });
|
|
57
87
|
|
|
58
88
|
// Execute the generated JavaScript (with stdlib)
|
|
59
89
|
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
|
|
@@ -81,26 +111,18 @@ export async function runFile(filePath, options = {}) {
|
|
|
81
111
|
compiled.add(filePath);
|
|
82
112
|
|
|
83
113
|
const depSource = readFileSync(filePath, 'utf-8');
|
|
114
|
+
const depImportPaths = findLocalTovaImports(depSource, filePath);
|
|
84
115
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
let transitiveMatch;
|
|
88
|
-
while ((transitiveMatch = transitiveRegex.exec(depSource)) !== null) {
|
|
89
|
-
const importSource = transitiveMatch[1];
|
|
90
|
-
if (!importSource.startsWith('.') && !importSource.startsWith('/')) continue;
|
|
91
|
-
let transitivePath = resolve(dirname(filePath), importSource);
|
|
92
|
-
if (!transitivePath.endsWith('.tova') && existsSync(transitivePath + '.tova')) {
|
|
93
|
-
transitivePath = transitivePath + '.tova';
|
|
94
|
-
}
|
|
95
|
-
if (transitivePath.endsWith('.tova') && existsSync(transitivePath)) {
|
|
96
|
-
resolveTovaImportsRecursive(transitivePath);
|
|
97
|
-
}
|
|
116
|
+
for (const depImport of depImportPaths) {
|
|
117
|
+
resolveTovaImportsRecursive(depImport.resolved);
|
|
98
118
|
}
|
|
99
119
|
|
|
100
120
|
// Compile this dependency
|
|
101
121
|
const dep = compileTova(depSource, filePath, { strict: options.strict });
|
|
102
122
|
let depShared = dep.shared || '';
|
|
103
123
|
depShared = depShared.replace(/^export /gm, '');
|
|
124
|
+
depShared = stripInlinedImports(depShared, depImportPaths.map(imp => imp.source));
|
|
125
|
+
depShared = rewriteImportsForRuntime(depShared, pathToFileURL(filePath).href);
|
|
104
126
|
depCode += depShared + '\n';
|
|
105
127
|
};
|
|
106
128
|
|
|
@@ -112,14 +134,8 @@ export async function runFile(filePath, options = {}) {
|
|
|
112
134
|
let code = stdlib + '\n' + depCode + (output.shared || '') + '\n' + (output.server || output.browser || '');
|
|
113
135
|
// Strip 'export ' keywords — not valid inside AsyncFunction (used in tova build only)
|
|
114
136
|
code = code.replace(/^export /gm, '');
|
|
115
|
-
|
|
116
|
-
code = code
|
|
117
|
-
if (hasTovaImports) {
|
|
118
|
-
for (const imp of tovaImportPaths) {
|
|
119
|
-
const escaped = imp.source.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
120
|
-
code = code.replace(new RegExp('^import\\s+(?:\\{[^}]*\\}|[\\w$]+|\\*\\s+as\\s+[\\w$]+)\\s+from\\s+[\'"]' + escaped + '[\'"];?\\s*$', 'gm'), '');
|
|
121
|
-
}
|
|
122
|
-
}
|
|
137
|
+
code = stripInlinedImports(code, tovaImportPaths.map(imp => imp.source));
|
|
138
|
+
code = rewriteImportsForRuntime(code, fileUrl);
|
|
123
139
|
// Auto-call main() if the compiled code defines a main function
|
|
124
140
|
const scriptArgs = options.scriptArgs || [];
|
|
125
141
|
if (/\bfunction\s+main\s*\(/.test(code)) {
|
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.11.
|
|
2
|
+
export const VERSION = "0.11.22";
|