fuoco 0.1.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/LICENSE +21 -0
- package/README.md +73 -0
- package/dist/cjs/index.mjs +82 -0
- package/dist/cjs/index.mjs.map +1 -0
- package/dist/cli.mjs +437 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/esm/index.mjs +95 -0
- package/dist/esm/index.mjs.map +1 -0
- package/dist/loader.mjs +8 -0
- package/dist/loader.mjs.map +1 -0
- package/dist/source-map-CTnvohRq.js +50 -0
- package/dist/source-map-CTnvohRq.js.map +1 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Brayden Moon
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# tsox
|
|
2
|
+
|
|
3
|
+
Fast TypeScript runner for Node.js. Like [tsx](https://github.com/privatenumber/tsx) but uses [Rolldown/Oxc](https://rolldown.rs) (Rust) instead of esbuild (Go) for transforms.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add tsox
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
tsox file.ts # run a ts file
|
|
15
|
+
tsox watch server.ts # watch mode
|
|
16
|
+
tsox -e "const x: number = 1" # eval
|
|
17
|
+
tsox -p "1 + 2" # eval + print
|
|
18
|
+
tsox --test # node test runner w/ ts
|
|
19
|
+
tsox # repl
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
All node flags pass through (`--env-file`, `--inspect`, etc).
|
|
23
|
+
|
|
24
|
+
### As a loader
|
|
25
|
+
|
|
26
|
+
Skip the CLI overhead entirely:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
node --import tsox/loader file.ts
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Benchmarks
|
|
33
|
+
|
|
34
|
+
Measured with [hyperfine](https://github.com/sharkdp/hyperfine) on Node v25.6.1, macOS arm64. 30 runs, 3 warmup.
|
|
35
|
+
|
|
36
|
+
**Hooks-only** (no CLI process, just the loader -- apples-to-apples transform comparison):
|
|
37
|
+
|
|
38
|
+
| | tsx | tsox | |
|
|
39
|
+
|---|--:|--:|---|
|
|
40
|
+
| 1 file | 177ms | 126ms | 1.4x |
|
|
41
|
+
| 5 modules | 193ms | 120ms | 1.6x |
|
|
42
|
+
| 100 modules | 303ms | 148ms | **2.1x** |
|
|
43
|
+
|
|
44
|
+
**E2E** (full `tsox file.ts` vs `tsx file.ts`):
|
|
45
|
+
|
|
46
|
+
| | tsx | tsox | |
|
|
47
|
+
|---|--:|--:|---|
|
|
48
|
+
| 1 file | 190ms | 153ms | 1.2x |
|
|
49
|
+
| types-heavy | 208ms | 151ms | 1.4x |
|
|
50
|
+
| enums | 207ms | 146ms | 1.4x |
|
|
51
|
+
| 5 modules | 171ms | 143ms | 1.2x |
|
|
52
|
+
| 100 modules | 226ms | 164ms | 1.4x |
|
|
53
|
+
|
|
54
|
+
The gap grows with more modules since Oxc's per-file transform cost is way lower than esbuild's (~0.02ms vs ~0.31ms). E2E numbers are closer because both tools pay the same child-process spawn cost.
|
|
55
|
+
|
|
56
|
+
Run `bash bench/run.sh` to reproduce (needs hyperfine + tsx).
|
|
57
|
+
|
|
58
|
+
## How it works
|
|
59
|
+
|
|
60
|
+
Same architecture as tsx -- CLI spawns `node --no-strip-types --import <loader> file.ts`, loader registers ESM hooks via `module.register()`, hooks intercept `.ts`/`.tsx`/`.mts`/`.cts` imports and run them through Oxc's `transformSync`. CJS support via `Module._extensions` patching.
|
|
61
|
+
|
|
62
|
+
We import from `rolldown/utils` instead of `rolldown/experimental` which avoids loading the full bundler engine (~9ms vs ~44ms import time).
|
|
63
|
+
|
|
64
|
+
Also handles `.js` -> `.ts` remapping, extensionless resolution, directory/index resolution, and tsconfig paths (lazy-loaded so there's zero cost if you don't use them).
|
|
65
|
+
|
|
66
|
+
## Requirements
|
|
67
|
+
|
|
68
|
+
- Node >= 22.12.0
|
|
69
|
+
- Works as a drop-in tsx replacement (same CLI flags, same loader interface)
|
|
70
|
+
|
|
71
|
+
## License
|
|
72
|
+
|
|
73
|
+
MIT
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { i as __require, r as transformCode, t as inlineSourceMap } from "../source-map-CTnvohRq.js";
|
|
2
|
+
import Module from "node:module";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
5
|
+
|
|
6
|
+
//#region src/cjs/index.ts
|
|
7
|
+
const extensions = Module._extensions;
|
|
8
|
+
const tsExtensions = [
|
|
9
|
+
".ts",
|
|
10
|
+
".tsx",
|
|
11
|
+
".mts",
|
|
12
|
+
".cts"
|
|
13
|
+
];
|
|
14
|
+
function compileTypeScript(module, filename) {
|
|
15
|
+
const result = transformCode(filename, readFileSync(filename, "utf-8"), true);
|
|
16
|
+
let code = result.code;
|
|
17
|
+
if (result.map) code = inlineSourceMap(code, result.map);
|
|
18
|
+
module._compile(code, filename);
|
|
19
|
+
}
|
|
20
|
+
for (const ext of tsExtensions) extensions[ext] = compileTypeScript;
|
|
21
|
+
let pathsMatcher;
|
|
22
|
+
function getPathsMatcher() {
|
|
23
|
+
if (pathsMatcher !== void 0) return pathsMatcher;
|
|
24
|
+
const tsconfigPath = process.env.TSOX_TSCONFIG_PATH;
|
|
25
|
+
if (!tsconfigPath) {
|
|
26
|
+
pathsMatcher = null;
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
const { getTsconfig, createPathsMatcher } = __require("get-tsconfig");
|
|
31
|
+
const config = getTsconfig(tsconfigPath);
|
|
32
|
+
pathsMatcher = config ? createPathsMatcher(config) : null;
|
|
33
|
+
} catch {
|
|
34
|
+
pathsMatcher = null;
|
|
35
|
+
}
|
|
36
|
+
return pathsMatcher;
|
|
37
|
+
}
|
|
38
|
+
const originalResolveFilename = Module._resolveFilename;
|
|
39
|
+
Module._resolveFilename = function(request, parent, isMain, options) {
|
|
40
|
+
try {
|
|
41
|
+
return originalResolveFilename.call(this, request, parent, isMain, options);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (error?.code !== "MODULE_NOT_FOUND") throw error;
|
|
44
|
+
}
|
|
45
|
+
if (!request.startsWith(".") && !request.startsWith("/") && !request.startsWith("node:") && !/^[A-Za-z]:[\\/]/.test(request)) {
|
|
46
|
+
const matcher = getPathsMatcher();
|
|
47
|
+
if (matcher) {
|
|
48
|
+
const matches = matcher(request);
|
|
49
|
+
for (const match of matches) {
|
|
50
|
+
try {
|
|
51
|
+
return originalResolveFilename.call(this, match, parent, isMain, options);
|
|
52
|
+
} catch {}
|
|
53
|
+
for (const ext of tsExtensions) try {
|
|
54
|
+
return originalResolveFilename.call(this, match + ext, parent, isMain, options);
|
|
55
|
+
} catch {}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const ext = path.extname(request);
|
|
60
|
+
const tsAlts = {
|
|
61
|
+
".js": [".ts", ".tsx"],
|
|
62
|
+
".cjs": [".cts"],
|
|
63
|
+
".mjs": [".mts"]
|
|
64
|
+
};
|
|
65
|
+
if (tsAlts[ext]) {
|
|
66
|
+
const base = request.slice(0, -ext.length);
|
|
67
|
+
for (const tsExt of tsAlts[ext]) try {
|
|
68
|
+
return originalResolveFilename.call(this, base + tsExt, parent, isMain, options);
|
|
69
|
+
} catch {}
|
|
70
|
+
}
|
|
71
|
+
for (const tsExt of tsExtensions) try {
|
|
72
|
+
return originalResolveFilename.call(this, request + tsExt, parent, isMain, options);
|
|
73
|
+
} catch {}
|
|
74
|
+
for (const tsExt of tsExtensions) try {
|
|
75
|
+
return originalResolveFilename.call(this, path.join(request, "index" + tsExt), parent, isMain, options);
|
|
76
|
+
} catch {}
|
|
77
|
+
return originalResolveFilename.call(this, request, parent, isMain, options);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
//#endregion
|
|
81
|
+
export { };
|
|
82
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/cjs/index.ts"],"sourcesContent":["// CJS loader — hooks into require() for TypeScript files\n\nimport Module from 'node:module';\nimport { readFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { transformCode } from '../utils/transform.ts';\nimport { inlineSourceMap } from '../utils/source-map.ts';\n\nconst extensions = (Module as any)._extensions as Record<string, Function>;\nconst tsExtensions = ['.ts', '.tsx', '.mts', '.cts'];\n\nfunction compileTypeScript(module: any, filename: string): void {\n const source = readFileSync(filename, 'utf-8');\n const result = transformCode(filename, source, true);\n\n let code = result.code;\n if (result.map) {\n code = inlineSourceMap(code, result.map);\n }\n\n module._compile(code, filename);\n}\n\nfor (const ext of tsExtensions) {\n extensions[ext] = compileTypeScript;\n}\n\n// lazy tsconfig paths\n\nlet pathsMatcher: ((s: string) => string[]) | null | undefined;\n\nfunction getPathsMatcher(): ((s: string) => string[]) | null {\n if (pathsMatcher !== undefined) return pathsMatcher;\n const tsconfigPath = process.env.TSOX_TSCONFIG_PATH;\n if (!tsconfigPath) {\n pathsMatcher = null;\n return null;\n }\n try {\n const { getTsconfig, createPathsMatcher } = require('get-tsconfig');\n const config = getTsconfig(tsconfigPath);\n pathsMatcher = config ? createPathsMatcher(config) : null;\n } catch {\n pathsMatcher = null;\n }\n return pathsMatcher;\n}\n\nconst originalResolveFilename = (Module as any)._resolveFilename;\n(Module as any)._resolveFilename = function (\n request: string,\n parent: any,\n isMain: boolean,\n options: any,\n) {\n try {\n return originalResolveFilename.call(this, request, parent, isMain, options);\n } catch (error: any) {\n if (error?.code !== 'MODULE_NOT_FOUND') throw error;\n }\n\n // tsconfig paths — skip for relative, absolute, and node: specifiers\n if (!request.startsWith('.') && !request.startsWith('/') && !request.startsWith('node:') && !/^[A-Za-z]:[\\\\/]/.test(request)) {\n const matcher = getPathsMatcher();\n if (matcher) {\n const matches = matcher(request);\n for (const match of matches) {\n try { return originalResolveFilename.call(this, match, parent, isMain, options); } catch {}\n for (const ext of tsExtensions) {\n try { return originalResolveFilename.call(this, match + ext, parent, isMain, options); } catch {}\n }\n }\n }\n }\n\n // .js → .ts\n const ext = path.extname(request);\n const tsAlts: Record<string, string[]> = { '.js': ['.ts', '.tsx'], '.cjs': ['.cts'], '.mjs': ['.mts'] };\n if (tsAlts[ext]) {\n const base = request.slice(0, -ext.length);\n for (const tsExt of tsAlts[ext]!) {\n try { return originalResolveFilename.call(this, base + tsExt, parent, isMain, options); } catch {}\n }\n }\n\n // extensionless\n for (const tsExt of tsExtensions) {\n try { return originalResolveFilename.call(this, request + tsExt, parent, isMain, options); } catch {}\n }\n\n // directory/index\n for (const tsExt of tsExtensions) {\n try { return originalResolveFilename.call(this, path.join(request, 'index' + tsExt), parent, isMain, options); } catch {}\n }\n\n return originalResolveFilename.call(this, request, parent, isMain, options);\n};\n"],"mappings":";;;;;;AAQA,MAAM,aAAc,OAAe;AACnC,MAAM,eAAe;CAAC;CAAO;CAAQ;CAAQ;CAAO;AAEpD,SAAS,kBAAkB,QAAa,UAAwB;CAE9D,MAAM,SAAS,cAAc,UADd,aAAa,UAAU,QAAQ,EACC,KAAK;CAEpD,IAAI,OAAO,OAAO;AAClB,KAAI,OAAO,IACT,QAAO,gBAAgB,MAAM,OAAO,IAAI;AAG1C,QAAO,SAAS,MAAM,SAAS;;AAGjC,KAAK,MAAM,OAAO,aAChB,YAAW,OAAO;AAKpB,IAAI;AAEJ,SAAS,kBAAoD;AAC3D,KAAI,iBAAiB,OAAW,QAAO;CACvC,MAAM,eAAe,QAAQ,IAAI;AACjC,KAAI,CAAC,cAAc;AACjB,iBAAe;AACf,SAAO;;AAET,KAAI;EACF,MAAM,EAAE,aAAa,iCAA+B,eAAe;EACnE,MAAM,SAAS,YAAY,aAAa;AACxC,iBAAe,SAAS,mBAAmB,OAAO,GAAG;SAC/C;AACN,iBAAe;;AAEjB,QAAO;;AAGT,MAAM,0BAA2B,OAAe;AAChD,AAAC,OAAe,mBAAmB,SACjC,SACA,QACA,QACA,SACA;AACA,KAAI;AACF,SAAO,wBAAwB,KAAK,MAAM,SAAS,QAAQ,QAAQ,QAAQ;UACpE,OAAY;AACnB,MAAI,OAAO,SAAS,mBAAoB,OAAM;;AAIhD,KAAI,CAAC,QAAQ,WAAW,IAAI,IAAI,CAAC,QAAQ,WAAW,IAAI,IAAI,CAAC,QAAQ,WAAW,QAAQ,IAAI,CAAC,kBAAkB,KAAK,QAAQ,EAAE;EAC5H,MAAM,UAAU,iBAAiB;AACjC,MAAI,SAAS;GACX,MAAM,UAAU,QAAQ,QAAQ;AAChC,QAAK,MAAM,SAAS,SAAS;AAC3B,QAAI;AAAE,YAAO,wBAAwB,KAAK,MAAM,OAAO,QAAQ,QAAQ,QAAQ;YAAU;AACzF,SAAK,MAAM,OAAO,aAChB,KAAI;AAAE,YAAO,wBAAwB,KAAK,MAAM,QAAQ,KAAK,QAAQ,QAAQ,QAAQ;YAAU;;;;CAOvG,MAAM,MAAM,KAAK,QAAQ,QAAQ;CACjC,MAAM,SAAmC;EAAE,OAAO,CAAC,OAAO,OAAO;EAAE,QAAQ,CAAC,OAAO;EAAE,QAAQ,CAAC,OAAO;EAAE;AACvG,KAAI,OAAO,MAAM;EACf,MAAM,OAAO,QAAQ,MAAM,GAAG,CAAC,IAAI,OAAO;AAC1C,OAAK,MAAM,SAAS,OAAO,KACzB,KAAI;AAAE,UAAO,wBAAwB,KAAK,MAAM,OAAO,OAAO,QAAQ,QAAQ,QAAQ;UAAU;;AAKpG,MAAK,MAAM,SAAS,aAClB,KAAI;AAAE,SAAO,wBAAwB,KAAK,MAAM,UAAU,OAAO,QAAQ,QAAQ,QAAQ;SAAU;AAIrG,MAAK,MAAM,SAAS,aAClB,KAAI;AAAE,SAAO,wBAAwB,KAAK,MAAM,KAAK,KAAK,SAAS,UAAU,MAAM,EAAE,QAAQ,QAAQ,QAAQ;SAAU;AAGzH,QAAO,wBAAwB,KAAK,MAAM,SAAS,QAAQ,QAAQ,QAAQ"}
|
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { constants } from "node:os";
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import fs, { watch } from "node:fs";
|
|
7
|
+
import { transformSync } from "rolldown/utils";
|
|
8
|
+
|
|
9
|
+
//#region src/watch/index.ts
|
|
10
|
+
const DEFAULT_EXCLUDE = [
|
|
11
|
+
"node_modules",
|
|
12
|
+
"bower_components",
|
|
13
|
+
"vendor",
|
|
14
|
+
"dist",
|
|
15
|
+
".git",
|
|
16
|
+
".svn",
|
|
17
|
+
".hg"
|
|
18
|
+
];
|
|
19
|
+
function startWatch(options) {
|
|
20
|
+
const { script, args, nodeArgs, include = [], exclude = DEFAULT_EXCLUDE, clearScreen = true, tsconfigPath, noCache } = options;
|
|
21
|
+
let child = null;
|
|
22
|
+
let restarting = false;
|
|
23
|
+
const watchers = [];
|
|
24
|
+
const cwd = process.cwd();
|
|
25
|
+
function buildArgs() {
|
|
26
|
+
return [
|
|
27
|
+
...nodeArgs,
|
|
28
|
+
"--import",
|
|
29
|
+
`data:text/javascript,${encodeURIComponent(buildLoaderCode(tsconfigPath, noCache))}`,
|
|
30
|
+
script,
|
|
31
|
+
...args
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
function startChild() {
|
|
35
|
+
const execArgs = buildArgs();
|
|
36
|
+
child = spawn(process.execPath, execArgs, {
|
|
37
|
+
stdio: "inherit",
|
|
38
|
+
env: {
|
|
39
|
+
...process.env,
|
|
40
|
+
...tsconfigPath ? { TSOX_TSCONFIG_PATH: tsconfigPath } : {},
|
|
41
|
+
...noCache ? { TSOX_DISABLE_CACHE: "1" } : {}
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
child.on("exit", (code) => {
|
|
45
|
+
if (!restarting) process.exitCode = code ?? 1;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
function restart() {
|
|
49
|
+
restarting = true;
|
|
50
|
+
if (clearScreen) process.stdout.write("\x1B[2J\x1B[0;0H");
|
|
51
|
+
logInfo("Restarting...");
|
|
52
|
+
if (child) {
|
|
53
|
+
child.removeAllListeners();
|
|
54
|
+
child.kill("SIGTERM");
|
|
55
|
+
const forceKillTimer = setTimeout(() => {
|
|
56
|
+
if (child && !child.killed) child.kill("SIGKILL");
|
|
57
|
+
}, 2e3);
|
|
58
|
+
child.on("exit", () => {
|
|
59
|
+
clearTimeout(forceKillTimer);
|
|
60
|
+
child = null;
|
|
61
|
+
restarting = false;
|
|
62
|
+
startChild();
|
|
63
|
+
});
|
|
64
|
+
} else {
|
|
65
|
+
restarting = false;
|
|
66
|
+
startChild();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function shouldIgnore(filename) {
|
|
70
|
+
const normalized = filename.replace(/\\/g, "/");
|
|
71
|
+
for (const pattern of exclude) if (normalized.includes(`/${pattern}/`) || normalized.startsWith(pattern + "/") || normalized === pattern) return true;
|
|
72
|
+
const basename = path.basename(normalized);
|
|
73
|
+
if (basename.startsWith(".") && basename !== "." && basename !== "..") return true;
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
function setupWatchers() {
|
|
77
|
+
const watchDirs = /* @__PURE__ */ new Set();
|
|
78
|
+
watchDirs.add(cwd);
|
|
79
|
+
for (const inc of include) watchDirs.add(path.dirname(path.resolve(inc)));
|
|
80
|
+
const relevantExtensions = new Set([
|
|
81
|
+
".ts",
|
|
82
|
+
".tsx",
|
|
83
|
+
".mts",
|
|
84
|
+
".cts",
|
|
85
|
+
".js",
|
|
86
|
+
".jsx",
|
|
87
|
+
".mjs",
|
|
88
|
+
".cjs",
|
|
89
|
+
".json"
|
|
90
|
+
]);
|
|
91
|
+
for (const dir of watchDirs) try {
|
|
92
|
+
const watcher = watch(dir, { recursive: true }, (_eventType, filename) => {
|
|
93
|
+
if (!filename || shouldIgnore(filename)) return;
|
|
94
|
+
if (relevantExtensions.has(path.extname(filename))) restart();
|
|
95
|
+
});
|
|
96
|
+
watchers.push(watcher);
|
|
97
|
+
} catch {}
|
|
98
|
+
}
|
|
99
|
+
if (process.stdin.isTTY) {
|
|
100
|
+
process.stdin.setRawMode(true);
|
|
101
|
+
process.stdin.resume();
|
|
102
|
+
process.stdin.setEncoding("utf-8");
|
|
103
|
+
process.stdin.on("data", (key) => {
|
|
104
|
+
if (key === "\r" || key === "\n") restart();
|
|
105
|
+
else if (key === "") {
|
|
106
|
+
cleanup();
|
|
107
|
+
process.exit(0);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
function cleanup() {
|
|
112
|
+
for (const w of watchers) w.close();
|
|
113
|
+
if (child && !child.killed) child.kill("SIGTERM");
|
|
114
|
+
}
|
|
115
|
+
process.on("SIGINT", () => {
|
|
116
|
+
cleanup();
|
|
117
|
+
process.exit(130);
|
|
118
|
+
});
|
|
119
|
+
process.on("SIGTERM", () => {
|
|
120
|
+
cleanup();
|
|
121
|
+
process.exit(143);
|
|
122
|
+
});
|
|
123
|
+
logInfo(`Watching for changes...`);
|
|
124
|
+
setupWatchers();
|
|
125
|
+
startChild();
|
|
126
|
+
}
|
|
127
|
+
function buildLoaderCode(tsconfigPath, noCache) {
|
|
128
|
+
const envLines = [];
|
|
129
|
+
if (tsconfigPath) envLines.push(`process.env.TSOX_TSCONFIG_PATH=${JSON.stringify(tsconfigPath)};`);
|
|
130
|
+
if (noCache) envLines.push(`process.env.TSOX_DISABLE_CACHE='1';`);
|
|
131
|
+
return `${envLines.join("")}import('tsox/loader');`;
|
|
132
|
+
}
|
|
133
|
+
function logInfo(msg) {
|
|
134
|
+
process.stderr.write(`\x1b[2m[\x1b[36mtsox\x1b[0m\x1b[2m]\x1b[0m ${msg}\n`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
//#endregion
|
|
138
|
+
//#region src/cli.ts
|
|
139
|
+
const VERSION = "0.1.0";
|
|
140
|
+
function resolveLoaderPath() {
|
|
141
|
+
const dir = fileURLToPath(new URL(".", import.meta.url));
|
|
142
|
+
const mjsPath = path.join(dir, "loader.mjs");
|
|
143
|
+
if (fs.existsSync(mjsPath)) return mjsPath;
|
|
144
|
+
const tsPath = path.join(dir, "loader.ts");
|
|
145
|
+
if (fs.existsSync(tsPath)) return tsPath;
|
|
146
|
+
return mjsPath;
|
|
147
|
+
}
|
|
148
|
+
function parseArgs(argv) {
|
|
149
|
+
const result = {
|
|
150
|
+
command: "run",
|
|
151
|
+
scriptArgs: [],
|
|
152
|
+
nodeArgs: [],
|
|
153
|
+
noCache: false,
|
|
154
|
+
test: false,
|
|
155
|
+
clearScreen: true,
|
|
156
|
+
watchInclude: [],
|
|
157
|
+
watchExclude: []
|
|
158
|
+
};
|
|
159
|
+
let i = 0;
|
|
160
|
+
let foundScript = false;
|
|
161
|
+
let isWatchMode = false;
|
|
162
|
+
while (i < argv.length) {
|
|
163
|
+
const arg = argv[i];
|
|
164
|
+
if (foundScript) {
|
|
165
|
+
result.scriptArgs.push(arg);
|
|
166
|
+
i++;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (arg === "watch" && !foundScript) {
|
|
170
|
+
isWatchMode = true;
|
|
171
|
+
result.command = "watch";
|
|
172
|
+
i++;
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (arg === "--version" || arg === "-v") {
|
|
176
|
+
result.command = "version";
|
|
177
|
+
return result;
|
|
178
|
+
}
|
|
179
|
+
if (arg === "--help" || arg === "-h") {
|
|
180
|
+
result.command = "help";
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
if (arg === "--no-cache") {
|
|
184
|
+
result.noCache = true;
|
|
185
|
+
i++;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (arg === "--tsconfig" && i + 1 < argv.length) {
|
|
189
|
+
result.tsconfig = argv[++i];
|
|
190
|
+
i++;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (arg.startsWith("--tsconfig=")) {
|
|
194
|
+
result.tsconfig = arg.slice(11);
|
|
195
|
+
i++;
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if (isWatchMode) {
|
|
199
|
+
if (arg === "--clear-screen=false") {
|
|
200
|
+
result.clearScreen = false;
|
|
201
|
+
i++;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
if (arg === "--include" && i + 1 < argv.length) {
|
|
205
|
+
result.watchInclude.push(argv[++i]);
|
|
206
|
+
i++;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (arg.startsWith("--include=")) {
|
|
210
|
+
result.watchInclude.push(arg.slice(10));
|
|
211
|
+
i++;
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (arg === "--exclude" && i + 1 < argv.length) {
|
|
215
|
+
result.watchExclude.push(argv[++i]);
|
|
216
|
+
i++;
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (arg.startsWith("--exclude=")) {
|
|
220
|
+
result.watchExclude.push(arg.slice(10));
|
|
221
|
+
i++;
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (arg === "-e" || arg === "--eval") {
|
|
226
|
+
result.eval = argv[++i];
|
|
227
|
+
i++;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (arg === "-p" || arg === "--print") {
|
|
231
|
+
result.print = argv[++i];
|
|
232
|
+
i++;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (arg === "--input-type" && i + 1 < argv.length) {
|
|
236
|
+
result.inputType = argv[++i];
|
|
237
|
+
i++;
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
if (arg.startsWith("--input-type=")) {
|
|
241
|
+
result.inputType = arg.slice(13);
|
|
242
|
+
i++;
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
if (arg === "--test") {
|
|
246
|
+
result.test = true;
|
|
247
|
+
i++;
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
if (arg === "-i" || arg === "--interactive") {
|
|
251
|
+
result.command = "repl";
|
|
252
|
+
i++;
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
if (arg.startsWith("-")) {
|
|
256
|
+
if ([
|
|
257
|
+
"--require",
|
|
258
|
+
"-r",
|
|
259
|
+
"--import",
|
|
260
|
+
"--env-file",
|
|
261
|
+
"--conditions",
|
|
262
|
+
"-C"
|
|
263
|
+
].some((f) => arg === f) && i + 1 < argv.length) {
|
|
264
|
+
result.nodeArgs.push(arg, argv[++i]);
|
|
265
|
+
i++;
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
result.nodeArgs.push(arg);
|
|
269
|
+
i++;
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
result.script = arg;
|
|
273
|
+
foundScript = true;
|
|
274
|
+
i++;
|
|
275
|
+
}
|
|
276
|
+
if (!result.script && !result.eval && !result.print && !result.test) result.command = "repl";
|
|
277
|
+
return result;
|
|
278
|
+
}
|
|
279
|
+
const args = parseArgs(process.argv.slice(2));
|
|
280
|
+
switch (args.command) {
|
|
281
|
+
case "version":
|
|
282
|
+
process.stdout.write(`tsox v${VERSION}\nnode ${process.version}\n`);
|
|
283
|
+
break;
|
|
284
|
+
case "help":
|
|
285
|
+
printHelp();
|
|
286
|
+
break;
|
|
287
|
+
case "repl":
|
|
288
|
+
handleRepl(args);
|
|
289
|
+
break;
|
|
290
|
+
case "watch":
|
|
291
|
+
handleWatch(args);
|
|
292
|
+
break;
|
|
293
|
+
default:
|
|
294
|
+
handleRun(args);
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
function handleRun(args) {
|
|
298
|
+
const env = { ...process.env };
|
|
299
|
+
if (args.tsconfig) env.TSOX_TSCONFIG_PATH = args.tsconfig;
|
|
300
|
+
if (args.noCache) env.TSOX_DISABLE_CACHE = "1";
|
|
301
|
+
const loaderURL = pathToFileURL(resolveLoaderPath()).href;
|
|
302
|
+
const nodeArgs = [
|
|
303
|
+
"--no-strip-types",
|
|
304
|
+
...args.nodeArgs,
|
|
305
|
+
"--import",
|
|
306
|
+
loaderURL
|
|
307
|
+
];
|
|
308
|
+
if (args.eval !== void 0 || args.print !== void 0) {
|
|
309
|
+
const evalType = args.print !== void 0 ? "print" : "eval";
|
|
310
|
+
const result = transformSync("/eval.ts", args.print ?? args.eval, { sourcemap: false });
|
|
311
|
+
const flag = evalType === "print" ? "--print" : "--eval";
|
|
312
|
+
nodeArgs.push(flag, result.code);
|
|
313
|
+
if (args.inputType) nodeArgs.push("--input-type", args.inputType);
|
|
314
|
+
relaySignals(spawnChild(nodeArgs, env));
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
if (args.test) {
|
|
318
|
+
nodeArgs.push("--test");
|
|
319
|
+
if (args.scriptArgs.length === 0 && !args.script) nodeArgs.push("**/{test,test/**/*,test-*,*[.-_]test}.?(c|m)@(t|j)s");
|
|
320
|
+
if (args.script) nodeArgs.push(args.script);
|
|
321
|
+
nodeArgs.push(...args.scriptArgs);
|
|
322
|
+
relaySignals(spawnChild(nodeArgs, env));
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
if (args.script) {
|
|
326
|
+
nodeArgs.push(args.script);
|
|
327
|
+
nodeArgs.push(...args.scriptArgs);
|
|
328
|
+
}
|
|
329
|
+
relaySignals(spawnChild(nodeArgs, env));
|
|
330
|
+
}
|
|
331
|
+
function handleWatch(args) {
|
|
332
|
+
if (!args.script) {
|
|
333
|
+
process.stderr.write("Error: watch mode requires a script path\n");
|
|
334
|
+
process.exit(1);
|
|
335
|
+
}
|
|
336
|
+
const loaderURL = pathToFileURL(resolveLoaderPath()).href;
|
|
337
|
+
startWatch({
|
|
338
|
+
script: args.script,
|
|
339
|
+
args: args.scriptArgs,
|
|
340
|
+
nodeArgs: [
|
|
341
|
+
"--no-strip-types",
|
|
342
|
+
...args.nodeArgs,
|
|
343
|
+
"--import",
|
|
344
|
+
loaderURL
|
|
345
|
+
],
|
|
346
|
+
include: args.watchInclude,
|
|
347
|
+
exclude: args.watchExclude.length > 0 ? args.watchExclude : void 0,
|
|
348
|
+
clearScreen: args.clearScreen,
|
|
349
|
+
tsconfigPath: args.tsconfig,
|
|
350
|
+
noCache: args.noCache
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
function handleRepl(args) {
|
|
354
|
+
const env = { ...process.env };
|
|
355
|
+
if (args.tsconfig) env.TSOX_TSCONFIG_PATH = args.tsconfig;
|
|
356
|
+
const loaderURL = pathToFileURL(resolveLoaderPath()).href;
|
|
357
|
+
relaySignals(spawnChild([
|
|
358
|
+
"--no-strip-types",
|
|
359
|
+
...args.nodeArgs,
|
|
360
|
+
"--import",
|
|
361
|
+
loaderURL,
|
|
362
|
+
"--interactive"
|
|
363
|
+
], env));
|
|
364
|
+
}
|
|
365
|
+
function spawnChild(nodeArgs, env) {
|
|
366
|
+
const stdio = [
|
|
367
|
+
"inherit",
|
|
368
|
+
"inherit",
|
|
369
|
+
"inherit"
|
|
370
|
+
];
|
|
371
|
+
if (process.send) stdio.push("ipc");
|
|
372
|
+
const child = spawn(process.execPath, nodeArgs, {
|
|
373
|
+
stdio,
|
|
374
|
+
env
|
|
375
|
+
});
|
|
376
|
+
if (process.send) {
|
|
377
|
+
child.on("message", (msg) => process.send(msg));
|
|
378
|
+
process.on("message", (msg) => {
|
|
379
|
+
if (child.send) child.send(msg);
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
child.on("close", (exitCode, signal) => {
|
|
383
|
+
if (exitCode === null && signal) {
|
|
384
|
+
const signalCode = constants.signals[signal] ?? 0;
|
|
385
|
+
process.exit(128 + signalCode);
|
|
386
|
+
}
|
|
387
|
+
process.exit(exitCode ?? 0);
|
|
388
|
+
});
|
|
389
|
+
return child;
|
|
390
|
+
}
|
|
391
|
+
function relaySignals(child) {
|
|
392
|
+
const relay = (signal) => {
|
|
393
|
+
if (!child.killed) child.kill(signal);
|
|
394
|
+
};
|
|
395
|
+
process.on("SIGINT", () => relay("SIGINT"));
|
|
396
|
+
process.on("SIGTERM", () => relay("SIGTERM"));
|
|
397
|
+
}
|
|
398
|
+
function printHelp() {
|
|
399
|
+
process.stdout.write(`
|
|
400
|
+
tsox v${VERSION} - fast typescript execution
|
|
401
|
+
|
|
402
|
+
Usage:
|
|
403
|
+
tsox [flags] <script.ts> [script args...]
|
|
404
|
+
tsox watch [flags] <script.ts> [script args...]
|
|
405
|
+
|
|
406
|
+
Commands:
|
|
407
|
+
watch Watch mode - restart on file changes
|
|
408
|
+
|
|
409
|
+
Flags:
|
|
410
|
+
--tsconfig <path> Custom tsconfig.json path
|
|
411
|
+
--no-cache Disable transform caching
|
|
412
|
+
-e, --eval <code> Evaluate TypeScript code
|
|
413
|
+
-p, --print <code> Evaluate and print result
|
|
414
|
+
--test Run node test runner with TypeScript
|
|
415
|
+
-v, --version Show version
|
|
416
|
+
-h, --help Show this help
|
|
417
|
+
|
|
418
|
+
Watch flags:
|
|
419
|
+
--include <path> Additional paths to watch
|
|
420
|
+
--exclude <path> Paths to exclude from watching
|
|
421
|
+
--clear-screen=false Disable screen clearing on restart
|
|
422
|
+
|
|
423
|
+
Examples:
|
|
424
|
+
tsox file.ts Run a TypeScript file
|
|
425
|
+
tsox --env-file=.env file.ts Run with environment file
|
|
426
|
+
tsox watch server.ts Watch mode
|
|
427
|
+
tsox -e "console.log(42)" Evaluate expression
|
|
428
|
+
tsox --test Run tests
|
|
429
|
+
tsox TypeScript REPL
|
|
430
|
+
|
|
431
|
+
All node flags are passed through.
|
|
432
|
+
`);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
//#endregion
|
|
436
|
+
export { };
|
|
437
|
+
//# sourceMappingURL=cli.mjs.map
|
package/dist/cli.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":["fsWatch","osConstants"],"sources":["../src/watch/index.ts","../src/cli.ts"],"sourcesContent":["import { watch as fsWatch, type FSWatcher } from 'node:fs';\nimport { spawn, type ChildProcess } from 'node:child_process';\nimport path from 'node:path';\n\nexport interface WatchOptions {\n script: string;\n args: string[];\n nodeArgs: string[];\n include?: string[];\n exclude?: string[];\n clearScreen?: boolean;\n tsconfigPath?: string;\n noCache?: boolean;\n}\n\nconst DEFAULT_EXCLUDE = [\n 'node_modules',\n 'bower_components',\n 'vendor',\n 'dist',\n '.git',\n '.svn',\n '.hg',\n];\n\nexport function startWatch(options: WatchOptions): void {\n const {\n script,\n args,\n nodeArgs,\n include = [],\n exclude = DEFAULT_EXCLUDE,\n clearScreen = true,\n tsconfigPath,\n noCache,\n } = options;\n\n let child: ChildProcess | null = null;\n let restarting = false;\n const watchers: FSWatcher[] = [];\n\n const cwd = process.cwd();\n\n function buildArgs(): string[] {\n return [\n ...nodeArgs,\n '--import',\n `data:text/javascript,${encodeURIComponent(buildLoaderCode(tsconfigPath, noCache))}`,\n script,\n ...args,\n ];\n }\n\n function startChild(): void {\n const execArgs = buildArgs();\n child = spawn(process.execPath, execArgs, {\n stdio: 'inherit',\n env: {\n ...process.env,\n ...(tsconfigPath ? { TSOX_TSCONFIG_PATH: tsconfigPath } : {}),\n ...(noCache ? { TSOX_DISABLE_CACHE: '1' } : {}),\n },\n });\n\n child.on('exit', (code) => {\n if (!restarting) {\n process.exitCode = code ?? 1;\n }\n });\n }\n\n function restart(): void {\n restarting = true;\n if (clearScreen) {\n process.stdout.write('\\x1B[2J\\x1B[0;0H');\n }\n logInfo('Restarting...');\n\n if (child) {\n child.removeAllListeners();\n child.kill('SIGTERM');\n\n const forceKillTimer = setTimeout(() => {\n if (child && !child.killed) child.kill('SIGKILL');\n }, 2000);\n\n child.on('exit', () => {\n clearTimeout(forceKillTimer);\n child = null;\n restarting = false;\n startChild();\n });\n } else {\n restarting = false;\n startChild();\n }\n }\n\n function shouldIgnore(filename: string): boolean {\n const normalized = filename.replace(/\\\\/g, '/');\n for (const pattern of exclude) {\n if (normalized.includes(`/${pattern}/`) || normalized.startsWith(pattern + '/') || normalized === pattern) {\n return true;\n }\n }\n const basename = path.basename(normalized);\n if (basename.startsWith('.') && basename !== '.' && basename !== '..') {\n return true;\n }\n return false;\n }\n\n function setupWatchers(): void {\n const watchDirs = new Set<string>();\n watchDirs.add(cwd);\n\n for (const inc of include) {\n watchDirs.add(path.dirname(path.resolve(inc)));\n }\n\n const relevantExtensions = new Set([\n '.ts', '.tsx', '.mts', '.cts',\n '.js', '.jsx', '.mjs', '.cjs',\n '.json',\n ]);\n\n for (const dir of watchDirs) {\n try {\n const watcher = fsWatch(dir, { recursive: true }, (_eventType, filename) => {\n if (!filename || shouldIgnore(filename)) return;\n if (relevantExtensions.has(path.extname(filename))) {\n restart();\n }\n });\n watchers.push(watcher);\n } catch {\n // dir might not exist yet\n }\n }\n }\n\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(true);\n process.stdin.resume();\n process.stdin.setEncoding('utf-8');\n process.stdin.on('data', (key: string) => {\n if (key === '\\r' || key === '\\n') {\n restart();\n } else if (key === '\\u0003') {\n cleanup();\n process.exit(0);\n }\n });\n }\n\n function cleanup(): void {\n for (const w of watchers) w.close();\n if (child && !child.killed) child.kill('SIGTERM');\n }\n\n process.on('SIGINT', () => { cleanup(); process.exit(130); });\n process.on('SIGTERM', () => { cleanup(); process.exit(143); });\n\n logInfo(`Watching for changes...`);\n setupWatchers();\n startChild();\n}\n\nfunction buildLoaderCode(tsconfigPath?: string, noCache?: boolean): string {\n const envLines: string[] = [];\n if (tsconfigPath) envLines.push(`process.env.TSOX_TSCONFIG_PATH=${JSON.stringify(tsconfigPath)};`);\n if (noCache) envLines.push(`process.env.TSOX_DISABLE_CACHE='1';`);\n return `${envLines.join('')}import('tsox/loader');`;\n}\n\nfunction logInfo(msg: string): void {\n process.stderr.write(`\\x1b[2m[\\x1b[36mtsox\\x1b[0m\\x1b[2m]\\x1b[0m ${msg}\\n`);\n}\n","#!/usr/bin/env node\n\n// tsox — fast tsx alternative using rolldown/oxc\n// spawns a child node process with our loader hooks registered via --import\n\nimport { spawn, type ChildProcess } from 'node:child_process';\nimport { constants as osConstants } from 'node:os';\nimport { pathToFileURL, fileURLToPath } from 'node:url';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport { transformSync } from 'rolldown/utils';\nimport { startWatch } from './watch/index.ts';\n\nconst VERSION = '0.1.0';\n\nfunction resolveLoaderPath(): string {\n const dir = fileURLToPath(new URL('.', import.meta.url));\n const mjsPath = path.join(dir, 'loader.mjs');\n if (fs.existsSync(mjsPath)) return mjsPath;\n const tsPath = path.join(dir, 'loader.ts');\n if (fs.existsSync(tsPath)) return tsPath;\n return mjsPath;\n}\n\ninterface ParsedArgs {\n command: 'run' | 'watch' | 'version' | 'help' | 'repl';\n script?: string;\n scriptArgs: string[];\n nodeArgs: string[];\n eval?: string;\n print?: string;\n inputType?: string;\n tsconfig?: string;\n noCache: boolean;\n test: boolean;\n clearScreen: boolean;\n watchInclude: string[];\n watchExclude: string[];\n}\n\nfunction parseArgs(argv: string[]): ParsedArgs {\n const result: ParsedArgs = {\n command: 'run',\n scriptArgs: [],\n nodeArgs: [],\n noCache: false,\n test: false,\n clearScreen: true,\n watchInclude: [],\n watchExclude: [],\n };\n\n let i = 0;\n let foundScript = false;\n let isWatchMode = false;\n\n while (i < argv.length) {\n const arg = argv[i]!;\n\n if (foundScript) {\n result.scriptArgs.push(arg);\n i++;\n continue;\n }\n\n if (arg === 'watch' && !foundScript) {\n isWatchMode = true;\n result.command = 'watch';\n i++;\n continue;\n }\n\n if (arg === '--version' || arg === '-v') {\n result.command = 'version';\n return result;\n }\n if (arg === '--help' || arg === '-h') {\n result.command = 'help';\n return result;\n }\n if (arg === '--no-cache') {\n result.noCache = true;\n i++;\n continue;\n }\n if (arg === '--tsconfig' && i + 1 < argv.length) {\n result.tsconfig = argv[++i];\n i++;\n continue;\n }\n if (arg.startsWith('--tsconfig=')) {\n result.tsconfig = arg.slice('--tsconfig='.length);\n i++;\n continue;\n }\n\n if (isWatchMode) {\n if (arg === '--clear-screen=false') {\n result.clearScreen = false;\n i++;\n continue;\n }\n if (arg === '--include' && i + 1 < argv.length) {\n result.watchInclude.push(argv[++i]!);\n i++;\n continue;\n }\n if (arg.startsWith('--include=')) {\n result.watchInclude.push(arg.slice('--include='.length));\n i++;\n continue;\n }\n if (arg === '--exclude' && i + 1 < argv.length) {\n result.watchExclude.push(argv[++i]!);\n i++;\n continue;\n }\n if (arg.startsWith('--exclude=')) {\n result.watchExclude.push(arg.slice('--exclude='.length));\n i++;\n continue;\n }\n }\n\n if (arg === '-e' || arg === '--eval') {\n result.eval = argv[++i];\n i++;\n continue;\n }\n if (arg === '-p' || arg === '--print') {\n result.print = argv[++i];\n i++;\n continue;\n }\n if (arg === '--input-type' && i + 1 < argv.length) {\n result.inputType = argv[++i];\n i++;\n continue;\n }\n if (arg.startsWith('--input-type=')) {\n result.inputType = arg.slice('--input-type='.length);\n i++;\n continue;\n }\n if (arg === '--test') {\n result.test = true;\n i++;\n continue;\n }\n if (arg === '-i' || arg === '--interactive') {\n result.command = 'repl';\n i++;\n continue;\n }\n\n if (arg.startsWith('-')) {\n const valueFlags = [\n '--require', '-r', '--import', '--env-file',\n '--conditions', '-C',\n ];\n if (valueFlags.some(f => arg === f) && i + 1 < argv.length) {\n result.nodeArgs.push(arg, argv[++i]!);\n i++;\n continue;\n }\n result.nodeArgs.push(arg);\n i++;\n continue;\n }\n\n result.script = arg;\n foundScript = true;\n i++;\n }\n\n if (!result.script && !result.eval && !result.print && !result.test) {\n result.command = 'repl';\n }\n\n return result;\n}\n\nconst args = parseArgs(process.argv.slice(2));\n\nswitch (args.command) {\n case 'version':\n process.stdout.write(`tsox v${VERSION}\\nnode ${process.version}\\n`);\n break;\n case 'help':\n printHelp();\n break;\n case 'repl':\n handleRepl(args);\n break;\n case 'watch':\n handleWatch(args);\n break;\n case 'run':\n default:\n handleRun(args);\n break;\n}\n\nfunction handleRun(args: ParsedArgs): void {\n const env: NodeJS.ProcessEnv = { ...process.env };\n if (args.tsconfig) env.TSOX_TSCONFIG_PATH = args.tsconfig;\n if (args.noCache) env.TSOX_DISABLE_CACHE = '1';\n\n const loaderURL = pathToFileURL(resolveLoaderPath()).href;\n\n const nodeArgs: string[] = [\n '--no-strip-types',\n ...args.nodeArgs,\n '--import', loaderURL,\n ];\n\n if (args.eval !== undefined || args.print !== undefined) {\n const evalType = args.print !== undefined ? 'print' : 'eval';\n const code = (args.print ?? args.eval)!;\n\n const result = transformSync('/eval.ts', code, {\n sourcemap: false,\n } as any);\n\n const flag = evalType === 'print' ? '--print' : '--eval';\n nodeArgs.push(flag, result.code);\n\n if (args.inputType) {\n nodeArgs.push('--input-type', args.inputType);\n }\n\n const child = spawnChild(nodeArgs, env);\n relaySignals(child);\n return;\n }\n\n if (args.test) {\n nodeArgs.push('--test');\n if (args.scriptArgs.length === 0 && !args.script) {\n nodeArgs.push('**/{test,test/**/*,test-*,*[.-_]test}.?(c|m)@(t|j)s');\n }\n if (args.script) nodeArgs.push(args.script);\n nodeArgs.push(...args.scriptArgs);\n\n const child = spawnChild(nodeArgs, env);\n relaySignals(child);\n return;\n }\n\n if (args.script) {\n nodeArgs.push(args.script);\n nodeArgs.push(...args.scriptArgs);\n }\n\n const child = spawnChild(nodeArgs, env);\n relaySignals(child);\n}\n\nfunction handleWatch(args: ParsedArgs): void {\n if (!args.script) {\n process.stderr.write('Error: watch mode requires a script path\\n');\n process.exit(1);\n }\n\n const loaderURL = pathToFileURL(resolveLoaderPath()).href;\n\n startWatch({\n script: args.script,\n args: args.scriptArgs,\n nodeArgs: ['--no-strip-types', ...args.nodeArgs, '--import', loaderURL],\n include: args.watchInclude,\n exclude: args.watchExclude.length > 0 ? args.watchExclude : undefined,\n clearScreen: args.clearScreen,\n tsconfigPath: args.tsconfig,\n noCache: args.noCache,\n });\n}\n\nfunction handleRepl(args: ParsedArgs): void {\n const env: NodeJS.ProcessEnv = { ...process.env };\n if (args.tsconfig) env.TSOX_TSCONFIG_PATH = args.tsconfig;\n\n const loaderURL = pathToFileURL(resolveLoaderPath()).href;\n\n const nodeArgs: string[] = [\n '--no-strip-types',\n ...args.nodeArgs,\n '--import', loaderURL,\n '--interactive',\n ];\n\n const child = spawnChild(nodeArgs, env);\n relaySignals(child);\n}\n\nfunction spawnChild(nodeArgs: string[], env: NodeJS.ProcessEnv): ChildProcess {\n const stdio: any[] = ['inherit', 'inherit', 'inherit'];\n if (process.send) stdio.push('ipc');\n\n const child = spawn(process.execPath, nodeArgs, { stdio, env });\n\n if (process.send) {\n child.on('message', (msg) => process.send!(msg));\n process.on('message', (msg) => {\n if (child.send) child.send(msg as any);\n });\n }\n\n child.on('close', (exitCode, signal) => {\n if (exitCode === null && signal) {\n const signalCode = (osConstants.signals as any)[signal] ?? 0;\n process.exit(128 + signalCode);\n }\n process.exit(exitCode ?? 0);\n });\n\n return child;\n}\n\nfunction relaySignals(child: ChildProcess): void {\n const relay = (signal: NodeJS.Signals) => {\n if (!child.killed) child.kill(signal);\n };\n process.on('SIGINT', () => relay('SIGINT'));\n process.on('SIGTERM', () => relay('SIGTERM'));\n}\n\nfunction printHelp(): void {\n process.stdout.write(`\ntsox v${VERSION} - fast typescript execution\n\nUsage:\n tsox [flags] <script.ts> [script args...]\n tsox watch [flags] <script.ts> [script args...]\n\nCommands:\n watch Watch mode - restart on file changes\n\nFlags:\n --tsconfig <path> Custom tsconfig.json path\n --no-cache Disable transform caching\n -e, --eval <code> Evaluate TypeScript code\n -p, --print <code> Evaluate and print result\n --test Run node test runner with TypeScript\n -v, --version Show version\n -h, --help Show this help\n\nWatch flags:\n --include <path> Additional paths to watch\n --exclude <path> Paths to exclude from watching\n --clear-screen=false Disable screen clearing on restart\n\nExamples:\n tsox file.ts Run a TypeScript file\n tsox --env-file=.env file.ts Run with environment file\n tsox watch server.ts Watch mode\n tsox -e \"console.log(42)\" Evaluate expression\n tsox --test Run tests\n tsox TypeScript REPL\n\nAll node flags are passed through.\n`);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { i as __require, n as isTypeScriptFile, r as transformCode, t as inlineSourceMap } from "../source-map-CTnvohRq.js";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
|
|
5
|
+
//#region src/esm/hooks.ts
|
|
6
|
+
const JS_TO_TS = {
|
|
7
|
+
".js": [".ts", ".tsx"],
|
|
8
|
+
".mjs": [".mts"],
|
|
9
|
+
".cjs": [".cts"]
|
|
10
|
+
};
|
|
11
|
+
const TS_EXTS = [
|
|
12
|
+
".ts",
|
|
13
|
+
".tsx",
|
|
14
|
+
".mts",
|
|
15
|
+
".cts"
|
|
16
|
+
];
|
|
17
|
+
let pathsMatcher;
|
|
18
|
+
function getPathsMatcher() {
|
|
19
|
+
if (pathsMatcher !== void 0) return pathsMatcher;
|
|
20
|
+
const tsconfigPath = process.env.TSOX_TSCONFIG_PATH;
|
|
21
|
+
if (!tsconfigPath) {
|
|
22
|
+
pathsMatcher = null;
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const { getTsconfig, createPathsMatcher } = __require("get-tsconfig");
|
|
27
|
+
const config = getTsconfig(tsconfigPath);
|
|
28
|
+
pathsMatcher = config ? createPathsMatcher(config) : null;
|
|
29
|
+
} catch {
|
|
30
|
+
pathsMatcher = null;
|
|
31
|
+
}
|
|
32
|
+
return pathsMatcher;
|
|
33
|
+
}
|
|
34
|
+
async function resolve(specifier, context, nextResolve) {
|
|
35
|
+
if (specifier.startsWith("node:") || specifier.startsWith("data:")) return nextResolve(specifier, context);
|
|
36
|
+
try {
|
|
37
|
+
return await nextResolve(specifier, context);
|
|
38
|
+
} catch (error) {
|
|
39
|
+
if (error?.code !== "ERR_MODULE_NOT_FOUND") throw error;
|
|
40
|
+
}
|
|
41
|
+
if (!specifier.startsWith(".") && !specifier.startsWith("/") && !specifier.startsWith("file:")) {
|
|
42
|
+
const matcher = getPathsMatcher();
|
|
43
|
+
if (matcher) {
|
|
44
|
+
const matches = matcher(specifier);
|
|
45
|
+
for (const match of matches) {
|
|
46
|
+
try {
|
|
47
|
+
return await nextResolve(match, context);
|
|
48
|
+
} catch {}
|
|
49
|
+
for (const ext of TS_EXTS) try {
|
|
50
|
+
return await nextResolve(match + ext, context);
|
|
51
|
+
} catch {}
|
|
52
|
+
for (const ext of TS_EXTS) try {
|
|
53
|
+
return await nextResolve(match + "/index" + ext, context);
|
|
54
|
+
} catch {}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const dotIdx = specifier.lastIndexOf(".");
|
|
59
|
+
if (dotIdx >= 0) {
|
|
60
|
+
const tsAlts = JS_TO_TS[specifier.slice(dotIdx)];
|
|
61
|
+
if (tsAlts) {
|
|
62
|
+
const base = specifier.slice(0, dotIdx);
|
|
63
|
+
for (const tsExt of tsAlts) try {
|
|
64
|
+
return await nextResolve(base + tsExt, context);
|
|
65
|
+
} catch {}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
for (const ext of TS_EXTS) try {
|
|
69
|
+
return await nextResolve(specifier + ext, context);
|
|
70
|
+
} catch {}
|
|
71
|
+
for (const ext of TS_EXTS) try {
|
|
72
|
+
return await nextResolve(specifier + "/index" + ext, context);
|
|
73
|
+
} catch {}
|
|
74
|
+
throw new Error(`[tsox] Cannot resolve '${specifier}'${context.parentURL ? ` from '${context.parentURL}'` : ""}`);
|
|
75
|
+
}
|
|
76
|
+
async function load(url, context, nextLoad) {
|
|
77
|
+
if (!url.startsWith("file:")) return nextLoad(url, context);
|
|
78
|
+
const filePath = fileURLToPath(url);
|
|
79
|
+
if (!isTypeScriptFile(filePath)) return nextLoad(url, context);
|
|
80
|
+
const result = transformCode(filePath, readFileSync(filePath, "utf-8"), true);
|
|
81
|
+
let code = result.code;
|
|
82
|
+
if (result.map) code = inlineSourceMap(code, result.map);
|
|
83
|
+
let format = context.format || "module";
|
|
84
|
+
if (filePath.endsWith(".cts")) format = "commonjs";
|
|
85
|
+
else if (filePath.endsWith(".mts")) format = "module";
|
|
86
|
+
return {
|
|
87
|
+
format,
|
|
88
|
+
source: code,
|
|
89
|
+
shortCircuit: true
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
//#endregion
|
|
94
|
+
export { load, resolve };
|
|
95
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/esm/hooks.ts"],"sourcesContent":["// ESM loader hooks — resolve + load\n// These run on every import in the process so fast-path bailouts matter.\n\nimport { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { transformCode, isTypeScriptFile } from '../utils/transform.ts';\nimport { inlineSourceMap } from '../utils/source-map.ts';\n\nconst JS_TO_TS: Record<string, string[]> = {\n '.js': ['.ts', '.tsx'],\n '.mjs': ['.mts'],\n '.cjs': ['.cts'],\n};\nconst TS_EXTS = ['.ts', '.tsx', '.mts', '.cts'];\n\nlet pathsMatcher: ((s: string) => string[]) | null | undefined;\n\nfunction getPathsMatcher(): ((s: string) => string[]) | null {\n if (pathsMatcher !== undefined) return pathsMatcher;\n const tsconfigPath = process.env.TSOX_TSCONFIG_PATH;\n if (!tsconfigPath) {\n pathsMatcher = null;\n return null;\n }\n try {\n const { getTsconfig, createPathsMatcher } = require('get-tsconfig');\n const config = getTsconfig(tsconfigPath);\n pathsMatcher = config ? createPathsMatcher(config) : null;\n } catch {\n pathsMatcher = null;\n }\n return pathsMatcher;\n}\n\nexport async function resolve(\n specifier: string,\n context: { parentURL?: string; conditions: string[] },\n nextResolve: Function,\n): Promise<{ url: string; shortCircuit?: boolean; format?: string }> {\n\n // builtins and data: urls don't need us\n if (specifier.startsWith('node:') || specifier.startsWith('data:')) {\n return nextResolve(specifier, context);\n }\n\n // try as-is first — this handles all node_modules, existing .js files, etc\n try {\n return await nextResolve(specifier, context);\n } catch (error: any) {\n if (error?.code !== 'ERR_MODULE_NOT_FOUND') throw error;\n }\n\n // import failed — try ts alternatives\n\n // tsconfig paths\n if (!specifier.startsWith('.') && !specifier.startsWith('/') && !specifier.startsWith('file:')) {\n const matcher = getPathsMatcher();\n if (matcher) {\n const matches = matcher(specifier);\n for (const match of matches) {\n try { return await nextResolve(match, context); } catch {}\n for (const ext of TS_EXTS) {\n try { return await nextResolve(match + ext, context); } catch {}\n }\n for (const ext of TS_EXTS) {\n try { return await nextResolve(match + '/index' + ext, context); } catch {}\n }\n }\n }\n }\n\n // .js → .ts remapping\n const dotIdx = specifier.lastIndexOf('.');\n if (dotIdx >= 0) {\n const ext = specifier.slice(dotIdx);\n const tsAlts = JS_TO_TS[ext];\n if (tsAlts) {\n const base = specifier.slice(0, dotIdx);\n for (const tsExt of tsAlts) {\n try { return await nextResolve(base + tsExt, context); } catch {}\n }\n }\n }\n\n // extensionless\n for (const ext of TS_EXTS) {\n try { return await nextResolve(specifier + ext, context); } catch {}\n }\n\n // directory/index\n for (const ext of TS_EXTS) {\n try { return await nextResolve(specifier + '/index' + ext, context); } catch {}\n }\n\n throw new Error(`[tsox] Cannot resolve '${specifier}'${context.parentURL ? ` from '${context.parentURL}'` : ''}`);\n}\n\nexport async function load(\n url: string,\n context: { format?: string; conditions: string[] },\n nextLoad: Function,\n): Promise<{ format: string; source: string; shortCircuit?: boolean }> {\n\n if (!url.startsWith('file:')) {\n return nextLoad(url, context);\n }\n\n const filePath = fileURLToPath(url);\n\n if (!isTypeScriptFile(filePath)) {\n return nextLoad(url, context);\n }\n\n const source = readFileSync(filePath, 'utf-8');\n const result = transformCode(filePath, source, true);\n\n let code = result.code;\n if (result.map) {\n code = inlineSourceMap(code, result.map);\n }\n\n let format = context.format || 'module';\n if (filePath.endsWith('.cts')) {\n format = 'commonjs';\n } else if (filePath.endsWith('.mts')) {\n format = 'module';\n }\n\n return { format, source: code, shortCircuit: true };\n}\n"],"mappings":";;;;;AAQA,MAAM,WAAqC;CACzC,OAAO,CAAC,OAAO,OAAO;CACtB,QAAQ,CAAC,OAAO;CAChB,QAAQ,CAAC,OAAO;CACjB;AACD,MAAM,UAAU;CAAC;CAAO;CAAQ;CAAQ;CAAO;AAE/C,IAAI;AAEJ,SAAS,kBAAoD;AAC3D,KAAI,iBAAiB,OAAW,QAAO;CACvC,MAAM,eAAe,QAAQ,IAAI;AACjC,KAAI,CAAC,cAAc;AACjB,iBAAe;AACf,SAAO;;AAET,KAAI;EACF,MAAM,EAAE,aAAa,iCAA+B,eAAe;EACnE,MAAM,SAAS,YAAY,aAAa;AACxC,iBAAe,SAAS,mBAAmB,OAAO,GAAG;SAC/C;AACN,iBAAe;;AAEjB,QAAO;;AAGT,eAAsB,QACpB,WACA,SACA,aACmE;AAGnE,KAAI,UAAU,WAAW,QAAQ,IAAI,UAAU,WAAW,QAAQ,CAChE,QAAO,YAAY,WAAW,QAAQ;AAIxC,KAAI;AACF,SAAO,MAAM,YAAY,WAAW,QAAQ;UACrC,OAAY;AACnB,MAAI,OAAO,SAAS,uBAAwB,OAAM;;AAMpD,KAAI,CAAC,UAAU,WAAW,IAAI,IAAI,CAAC,UAAU,WAAW,IAAI,IAAI,CAAC,UAAU,WAAW,QAAQ,EAAE;EAC9F,MAAM,UAAU,iBAAiB;AACjC,MAAI,SAAS;GACX,MAAM,UAAU,QAAQ,UAAU;AAClC,QAAK,MAAM,SAAS,SAAS;AAC3B,QAAI;AAAE,YAAO,MAAM,YAAY,OAAO,QAAQ;YAAU;AACxD,SAAK,MAAM,OAAO,QAChB,KAAI;AAAE,YAAO,MAAM,YAAY,QAAQ,KAAK,QAAQ;YAAU;AAEhE,SAAK,MAAM,OAAO,QAChB,KAAI;AAAE,YAAO,MAAM,YAAY,QAAQ,WAAW,KAAK,QAAQ;YAAU;;;;CAOjF,MAAM,SAAS,UAAU,YAAY,IAAI;AACzC,KAAI,UAAU,GAAG;EAEf,MAAM,SAAS,SADH,UAAU,MAAM,OAAO;AAEnC,MAAI,QAAQ;GACV,MAAM,OAAO,UAAU,MAAM,GAAG,OAAO;AACvC,QAAK,MAAM,SAAS,OAClB,KAAI;AAAE,WAAO,MAAM,YAAY,OAAO,OAAO,QAAQ;WAAU;;;AAMrE,MAAK,MAAM,OAAO,QAChB,KAAI;AAAE,SAAO,MAAM,YAAY,YAAY,KAAK,QAAQ;SAAU;AAIpE,MAAK,MAAM,OAAO,QAChB,KAAI;AAAE,SAAO,MAAM,YAAY,YAAY,WAAW,KAAK,QAAQ;SAAU;AAG/E,OAAM,IAAI,MAAM,0BAA0B,UAAU,GAAG,QAAQ,YAAY,UAAU,QAAQ,UAAU,KAAK,KAAK;;AAGnH,eAAsB,KACpB,KACA,SACA,UACqE;AAErE,KAAI,CAAC,IAAI,WAAW,QAAQ,CAC1B,QAAO,SAAS,KAAK,QAAQ;CAG/B,MAAM,WAAW,cAAc,IAAI;AAEnC,KAAI,CAAC,iBAAiB,SAAS,CAC7B,QAAO,SAAS,KAAK,QAAQ;CAI/B,MAAM,SAAS,cAAc,UADd,aAAa,UAAU,QAAQ,EACC,KAAK;CAEpD,IAAI,OAAO,OAAO;AAClB,KAAI,OAAO,IACT,QAAO,gBAAgB,MAAM,OAAO,IAAI;CAG1C,IAAI,SAAS,QAAQ,UAAU;AAC/B,KAAI,SAAS,SAAS,OAAO,CAC3B,UAAS;UACA,SAAS,SAAS,OAAO,CAClC,UAAS;AAGX,QAAO;EAAE;EAAQ,QAAQ;EAAM,cAAc;EAAM"}
|
package/dist/loader.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.mjs","names":[],"sources":["../src/loader.ts"],"sourcesContent":["import { register } from 'node:module';\n\n// Register ESM hooks in the loader thread\nregister('./esm/index.mjs', import.meta.url);\n"],"mappings":";;;AAGA,SAAS,mBAAmB,OAAO,KAAK,IAAI"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { transformSync } from "rolldown/utils";
|
|
3
|
+
|
|
4
|
+
//#region \0rolldown/runtime.js
|
|
5
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
6
|
+
|
|
7
|
+
//#endregion
|
|
8
|
+
//#region src/utils/transform.ts
|
|
9
|
+
const TS_EXTENSIONS = new Set([
|
|
10
|
+
".ts",
|
|
11
|
+
".tsx",
|
|
12
|
+
".mts",
|
|
13
|
+
".cts"
|
|
14
|
+
]);
|
|
15
|
+
const JSX_EXTENSIONS = new Set([".jsx", ".tsx"]);
|
|
16
|
+
function isTypeScriptFile(filename) {
|
|
17
|
+
const idx = filename.lastIndexOf(".");
|
|
18
|
+
if (idx < 0) return false;
|
|
19
|
+
return TS_EXTENSIONS.has(filename.slice(idx));
|
|
20
|
+
}
|
|
21
|
+
function isJSX(filename) {
|
|
22
|
+
const idx = filename.lastIndexOf(".");
|
|
23
|
+
if (idx < 0) return false;
|
|
24
|
+
return JSX_EXTENSIONS.has(filename.slice(idx));
|
|
25
|
+
}
|
|
26
|
+
function transformCode(filename, code, sourceMap = true) {
|
|
27
|
+
const result = transformSync(filename, code, {
|
|
28
|
+
sourcemap: sourceMap,
|
|
29
|
+
jsx: isJSX(filename) ? { runtime: "automatic" } : void 0
|
|
30
|
+
});
|
|
31
|
+
if (result.errors && result.errors.length > 0) {
|
|
32
|
+
const err = result.errors[0];
|
|
33
|
+
throw new Error(`[tsox] Transform error in ${filename}: ${typeof err === "string" ? err : JSON.stringify(err)}`);
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
code: result.code,
|
|
37
|
+
map: result.map ?? void 0
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/utils/source-map.ts
|
|
43
|
+
function inlineSourceMap(code, map) {
|
|
44
|
+
const mapJson = JSON.stringify(map);
|
|
45
|
+
return code + `\n//# sourceMappingURL=data:application/json;base64,${Buffer.from(mapJson).toString("base64")}\n`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
//#endregion
|
|
49
|
+
export { __require as i, isTypeScriptFile as n, transformCode as r, inlineSourceMap as t };
|
|
50
|
+
//# sourceMappingURL=source-map-CTnvohRq.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-map-CTnvohRq.js","names":[],"sources":["../src/utils/transform.ts","../src/utils/source-map.ts"],"sourcesContent":["import { transformSync } from 'rolldown/utils';\nimport type { TransformResult } from './types.ts';\n\nconst TS_EXTENSIONS = new Set(['.ts', '.tsx', '.mts', '.cts']);\nconst JSX_EXTENSIONS = new Set(['.jsx', '.tsx']);\n\nexport function isTypeScriptFile(filename: string): boolean {\n const idx = filename.lastIndexOf('.');\n if (idx < 0) return false;\n return TS_EXTENSIONS.has(filename.slice(idx));\n}\n\nfunction isJSX(filename: string): boolean {\n const idx = filename.lastIndexOf('.');\n if (idx < 0) return false;\n return JSX_EXTENSIONS.has(filename.slice(idx));\n}\n\nexport function transformCode(\n filename: string,\n code: string,\n sourceMap: boolean = true,\n): TransformResult {\n const result = transformSync(filename, code, {\n sourcemap: sourceMap,\n jsx: isJSX(filename) ? { runtime: 'automatic' } : undefined,\n } as any);\n\n if (result.errors && result.errors.length > 0) {\n const err = result.errors[0];\n throw new Error(`[tsox] Transform error in ${filename}: ${typeof err === 'string' ? err : JSON.stringify(err)}`);\n }\n\n return {\n code: result.code,\n map: result.map ?? undefined,\n };\n}\n","export function inlineSourceMap(\n code: string,\n map: object,\n): string {\n const mapJson = JSON.stringify(map);\n const mapBase64 = Buffer.from(mapJson).toString('base64');\n const sourceMapComment = `\\n//# sourceMappingURL=data:application/json;base64,${mapBase64}\\n`;\n return code + sourceMapComment;\n}\n"],"mappings":";;;;;;;;AAGA,MAAM,gBAAgB,IAAI,IAAI;CAAC;CAAO;CAAQ;CAAQ;CAAO,CAAC;AAC9D,MAAM,iBAAiB,IAAI,IAAI,CAAC,QAAQ,OAAO,CAAC;AAEhD,SAAgB,iBAAiB,UAA2B;CAC1D,MAAM,MAAM,SAAS,YAAY,IAAI;AACrC,KAAI,MAAM,EAAG,QAAO;AACpB,QAAO,cAAc,IAAI,SAAS,MAAM,IAAI,CAAC;;AAG/C,SAAS,MAAM,UAA2B;CACxC,MAAM,MAAM,SAAS,YAAY,IAAI;AACrC,KAAI,MAAM,EAAG,QAAO;AACpB,QAAO,eAAe,IAAI,SAAS,MAAM,IAAI,CAAC;;AAGhD,SAAgB,cACd,UACA,MACA,YAAqB,MACJ;CACjB,MAAM,SAAS,cAAc,UAAU,MAAM;EAC3C,WAAW;EACX,KAAK,MAAM,SAAS,GAAG,EAAE,SAAS,aAAa,GAAG;EACnD,CAAQ;AAET,KAAI,OAAO,UAAU,OAAO,OAAO,SAAS,GAAG;EAC7C,MAAM,MAAM,OAAO,OAAO;AAC1B,QAAM,IAAI,MAAM,6BAA6B,SAAS,IAAI,OAAO,QAAQ,WAAW,MAAM,KAAK,UAAU,IAAI,GAAG;;AAGlH,QAAO;EACL,MAAM,OAAO;EACb,KAAK,OAAO,OAAO;EACpB;;;;;ACpCH,SAAgB,gBACd,MACA,KACQ;CACR,MAAM,UAAU,KAAK,UAAU,IAAI;AAGnC,QAAO,OADkB,uDADP,OAAO,KAAK,QAAQ,CAAC,SAAS,SAAS,CACiC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fuoco",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Fast TypeScript runner. tsx alternative powered by Rolldown/Oxc.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"typescript",
|
|
7
|
+
"runner",
|
|
8
|
+
"node",
|
|
9
|
+
"esm",
|
|
10
|
+
"cjs",
|
|
11
|
+
"loader",
|
|
12
|
+
"watch",
|
|
13
|
+
"rolldown",
|
|
14
|
+
"oxc"
|
|
15
|
+
],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": "Brayden",
|
|
18
|
+
"type": "module",
|
|
19
|
+
"bin": {
|
|
20
|
+
"tsox": "dist/cli.mjs"
|
|
21
|
+
},
|
|
22
|
+
"main": "./dist/loader.mjs",
|
|
23
|
+
"exports": {
|
|
24
|
+
"./package.json": "./package.json",
|
|
25
|
+
".": "./dist/loader.mjs",
|
|
26
|
+
"./loader": "./dist/loader.mjs",
|
|
27
|
+
"./cjs": "./dist/cjs/index.cjs",
|
|
28
|
+
"./esm": "./dist/esm/index.mjs",
|
|
29
|
+
"./cli": "./dist/cli.mjs"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist"
|
|
33
|
+
],
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "node --strip-types --experimental-transform-types ./scripts/build.ts",
|
|
36
|
+
"dev": "node --strip-types --experimental-transform-types src/cli.ts",
|
|
37
|
+
"test": "node --strip-types --experimental-transform-types --test tests/index.ts"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=22.12.0"
|
|
41
|
+
},
|
|
42
|
+
"pnpm": {
|
|
43
|
+
"onlyBuiltDependencies": [
|
|
44
|
+
"esbuild"
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"get-tsconfig": "^4.10.0",
|
|
49
|
+
"rolldown": "^1.0.0-rc.5"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"tsx": "^4.21.0"
|
|
53
|
+
}
|
|
54
|
+
}
|