ttsc 0.4.0-dev.20260424 → 0.4.0-dev.20260425
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/README.md +63 -246
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/runner/cli.d.ts +1 -0
- package/lib/runner/cli.js +284 -0
- package/lib/runner/cli.js.map +1 -0
- package/lib/runner/register.d.ts +13 -0
- package/lib/runner/register.js +424 -0
- package/lib/runner/register.js.map +1 -0
- package/package.json +12 -10
- package/src/index.ts +1 -0
- package/src/launcher/ttsx.js +19 -0
- package/src/runner/cli.ts +286 -0
- package/src/runner/register.ts +487 -0
- package/test/package.test.ts +27 -19
package/README.md
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
# `ttsc`
|
|
2
2
|
|
|
3
|
-

|
|
4
4
|
|
|
5
5
|
[](https://github.com/samchon/ttsc/blob/master/LICENSE)
|
|
6
6
|
[](https://www.npmjs.com/package/ttsc)
|
|
7
7
|
[](https://www.npmjs.com/package/ttsc)
|
|
8
8
|
[](https://github.com/samchon/ttsc/actions?query=workflow%3Atest)
|
|
9
9
|
|
|
10
|
-
A `typescript-go` compiler
|
|
10
|
+
A `typescript-go` toolchain for compiler-powered transforms and type-safe execution.
|
|
11
11
|
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
12
|
+
- **`ttsc`**: build, check, and transform.
|
|
13
|
+
- **`ttsx`**: execute TypeScript with type checking.
|
|
14
|
+
- 10x faster than `ts-node`.
|
|
15
|
+
- type checking that `tsx` does not provide.
|
|
16
|
+
- **transformer support**: compiler-powered libraries, such as `typia`.
|
|
15
17
|
|
|
16
|
-
`
|
|
18
|
+
> `ttsx` (CLI command) can run existing `typescript@6` projects.
|
|
17
19
|
|
|
18
20
|
## Setup
|
|
19
21
|
|
|
@@ -23,18 +25,18 @@ Install the native TypeScript preview package with `ttsc`:
|
|
|
23
25
|
npm i -D ttsc @typescript/native-preview
|
|
24
26
|
```
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
Run TypeScript directly with `ttsx` (CLI command):
|
|
27
29
|
|
|
28
30
|
```bash
|
|
29
|
-
npx
|
|
30
|
-
npx ttsc --noEmit
|
|
31
|
-
npx ttsc --watch
|
|
31
|
+
npx ttsx src/index.ts
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
Build, check, or watch the project with `ttsc`:
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
|
-
npx ttsc
|
|
37
|
+
npx ttsc
|
|
38
|
+
npx ttsc --noEmit
|
|
39
|
+
npx ttsc --watch
|
|
38
40
|
```
|
|
39
41
|
|
|
40
42
|
## Transformer Configuration
|
|
@@ -51,7 +53,7 @@ npx ttsc transform --file src/index.ts
|
|
|
51
53
|
}
|
|
52
54
|
```
|
|
53
55
|
|
|
54
|
-
The same configuration is used by `ttsc`
|
|
56
|
+
The same configuration is used by both `ttsc` commands.
|
|
55
57
|
|
|
56
58
|
```bash
|
|
57
59
|
# compile
|
|
@@ -61,250 +63,65 @@ npx ttsc
|
|
|
61
63
|
npx ttsx src/index.ts
|
|
62
64
|
```
|
|
63
65
|
|
|
64
|
-
This gives compiler-powered libraries one transform path for build-time and runtime execution.
|
|
65
|
-
|
|
66
|
-
## CLI
|
|
66
|
+
This gives compiler-powered libraries one transform path for both build-time and runtime execution.
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
ttsc [options]
|
|
70
|
-
ttsc transform --file <path> [options]
|
|
71
|
-
ttsc version
|
|
72
|
-
```
|
|
68
|
+
## What Is a Transform?
|
|
73
69
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
```bash
|
|
77
|
-
ttsc
|
|
78
|
-
ttsc -p tsconfig.json
|
|
79
|
-
ttsc --noEmit
|
|
80
|
-
ttsc --watch
|
|
81
|
-
ttsc --outDir lib
|
|
82
|
-
ttsc --verbose
|
|
83
|
-
ttsc transform --file src/index.ts
|
|
84
|
-
ttsc transform --file src/index.ts --out tmp/index.js
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
Supported options:
|
|
88
|
-
|
|
89
|
-
- `-p, --project <file>`: resolve project settings from this config file.
|
|
90
|
-
- `--tsconfig <file>`: alias for an explicit project config file.
|
|
91
|
-
- `--cwd <dir>`: resolve project-relative paths from another directory.
|
|
92
|
-
- `--emit`: force emitted files during build.
|
|
93
|
-
- `--noEmit`: type check and transform without writing emitted files.
|
|
94
|
-
- `-w, --watch`: rebuild when project files change.
|
|
95
|
-
- `--preserveWatchOutput`: keep watch output on screen.
|
|
96
|
-
- `--outDir <dir>`: override `compilerOptions.outDir`.
|
|
97
|
-
- `--rewrite-mode <mode>`: force a native rewrite backend id.
|
|
98
|
-
- `--quiet`: keep native build output quiet.
|
|
99
|
-
- `--verbose`: print the native build summary and emitted files.
|
|
100
|
-
- `--out <path>`: write single-file transform output to a file.
|
|
101
|
-
- `--binary <path>`: use an explicit native backend binary.
|
|
102
|
-
|
|
103
|
-
Compatibility aliases:
|
|
104
|
-
|
|
105
|
-
```bash
|
|
106
|
-
ttsc build
|
|
107
|
-
ttsc check
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
`ttsc build` is the same project build lane as `ttsc`. `ttsc check` is the same as `ttsc --noEmit`.
|
|
111
|
-
|
|
112
|
-
## JS API
|
|
113
|
-
|
|
114
|
-
`ttsc` exposes a small JS API for bundlers, runners, and higher-level tools.
|
|
70
|
+
A transform uses TypeScript types to generate JavaScript before runtime.
|
|
115
71
|
|
|
116
72
|
```ts
|
|
117
|
-
import
|
|
118
|
-
|
|
73
|
+
import typia, { tags } from "typia";
|
|
74
|
+
import { v4 } from "uuid";
|
|
119
75
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const code = transform({
|
|
126
|
-
cwd: "/project",
|
|
127
|
-
file: "/project/src/index.ts",
|
|
128
|
-
tsconfig: "/project/tsconfig.json",
|
|
129
|
-
});
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
Build or check a project:
|
|
133
|
-
|
|
134
|
-
```ts
|
|
135
|
-
import { build, check } from "ttsc";
|
|
136
|
-
|
|
137
|
-
const built = build({
|
|
138
|
-
cwd: process.cwd(),
|
|
139
|
-
tsconfig: "tsconfig.json",
|
|
140
|
-
emit: true,
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
if (built.status !== 0) {
|
|
144
|
-
console.error(built.stderr);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const checked = check({
|
|
148
|
-
tsconfig: "tsconfig.json",
|
|
76
|
+
const matched: boolean = typia.is<IMember>({
|
|
77
|
+
id: v4(),
|
|
78
|
+
email: "samchon.github@gmail.com",
|
|
79
|
+
age: 30,
|
|
149
80
|
});
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
{
|
|
160
|
-
"compilerOptions": {
|
|
161
|
-
"plugins": [
|
|
162
|
-
{ "transform": "my-lib/lib/transform" }
|
|
163
|
-
]
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
The `transform` value may be:
|
|
169
|
-
|
|
170
|
-
- a package specifier.
|
|
171
|
-
- an absolute path.
|
|
172
|
-
- a relative path from the project root.
|
|
173
|
-
|
|
174
|
-
The plugin module may export a plugin object directly, a default export, a `plugin` export, or a `createTtscPlugin` factory.
|
|
175
|
-
|
|
176
|
-
```ts
|
|
177
|
-
import { definePlugin } from "ttsc";
|
|
178
|
-
|
|
179
|
-
export default definePlugin((config, context) => ({
|
|
180
|
-
name: "my-lib",
|
|
181
|
-
native: {
|
|
182
|
-
mode: "my-lib",
|
|
183
|
-
binary: require.resolve("my-lib-native/bin/ttsc-my-lib.js"),
|
|
184
|
-
contractVersion: 1,
|
|
185
|
-
capabilities: ["rewrite", "diagnostics"],
|
|
186
|
-
},
|
|
187
|
-
}));
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
The factory receives:
|
|
191
|
-
|
|
192
|
-
- `config`: the raw `compilerOptions.plugins[]` entry.
|
|
193
|
-
- `context.binary`: the fallback `ttsc` native binary path.
|
|
194
|
-
- `context.cwd`: the invocation working directory.
|
|
195
|
-
- `context.projectRoot`: the resolved project root.
|
|
196
|
-
- `context.tsconfig`: the resolved config file path.
|
|
197
|
-
|
|
198
|
-
Keep this Node-side entry small. Use it for package discovery, feature flags, native binary selection, and manifest construction. Put compiler-sensitive work in the native backend.
|
|
199
|
-
|
|
200
|
-
## Native Backend Contract
|
|
201
|
-
|
|
202
|
-
A native transform plugin describes the backend that owns type analysis and rewrite work.
|
|
203
|
-
|
|
204
|
-
```ts
|
|
205
|
-
import { definePlugin } from "ttsc";
|
|
206
|
-
|
|
207
|
-
export default definePlugin(() => ({
|
|
208
|
-
name: "my-lib",
|
|
209
|
-
native: {
|
|
210
|
-
mode: "my-lib",
|
|
211
|
-
binary: require.resolve("my-lib-native/bin/ttsc-my-lib.js"),
|
|
212
|
-
contractVersion: 1,
|
|
213
|
-
capabilities: ["rewrite", "diagnostics", "assets"],
|
|
214
|
-
},
|
|
215
|
-
}));
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
Descriptor shape:
|
|
219
|
-
|
|
220
|
-
```ts
|
|
221
|
-
interface TtscNativeBackend {
|
|
222
|
-
mode: string;
|
|
223
|
-
binary?: string;
|
|
224
|
-
contractVersion?: 1;
|
|
225
|
-
capabilities?: readonly string[];
|
|
81
|
+
console.log(matched); // true
|
|
82
|
+
|
|
83
|
+
interface IMember {
|
|
84
|
+
id: string & tags.Format<"uuid">;
|
|
85
|
+
email: string & tags.Format<"email">;
|
|
86
|
+
age: number &
|
|
87
|
+
tags.Type<"uint32"> &
|
|
88
|
+
tags.ExclusiveMinimum<19> &
|
|
89
|
+
tags.Maximum<100>;
|
|
226
90
|
}
|
|
227
91
|
```
|
|
228
92
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}));
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
`transformOutput()` receives JavaScript text after native emit. Use it for:
|
|
262
|
-
|
|
263
|
-
- banner injection.
|
|
264
|
-
- output string patching.
|
|
265
|
-
- runtime helper import rewrites.
|
|
266
|
-
- consumer-specific output normalization.
|
|
267
|
-
|
|
268
|
-
Use a native backend for compiler-sensitive work:
|
|
269
|
-
|
|
270
|
-
- type analysis.
|
|
271
|
-
- AST mutation.
|
|
272
|
-
- call-site recognition.
|
|
273
|
-
- `ts.Program` / `ts.TypeChecker` transformer compatibility.
|
|
274
|
-
|
|
275
|
-
## Current Constraints
|
|
276
|
-
|
|
277
|
-
These are current host constraints transformer authors should design around.
|
|
278
|
-
|
|
279
|
-
- One invocation can select one native `mode` / `binary` pair.
|
|
280
|
-
- Multiple text-only `transformOutput()` plugins can run in order.
|
|
281
|
-
- Passing `plugins` to the JS API replaces the tsconfig plugin list.
|
|
282
|
-
- `transformOutput()` is a JS text hook, not a JS AST hook.
|
|
283
|
-
- The current setup lane is `@typescript/native-preview`.
|
|
284
|
-
|
|
285
|
-
## Integration Patterns
|
|
286
|
-
|
|
287
|
-
Consumer library:
|
|
288
|
-
|
|
289
|
-
- publish a tsconfig plugin entry such as `my-lib/lib/transform`.
|
|
290
|
-
- resolve a consumer-owned native backend binary from that entry.
|
|
291
|
-
- keep type analysis and rewrite logic in the native backend.
|
|
292
|
-
- optionally layer `transformOutput()` for final JavaScript text normalization.
|
|
293
|
-
|
|
294
|
-
Bundler adapter:
|
|
295
|
-
|
|
296
|
-
- call `transform()` for per-file rewrites.
|
|
297
|
-
- pass `plugins` when the adapter needs deterministic plugin state.
|
|
298
|
-
- let `ttsc` resolve the native backend instead of spawning it manually.
|
|
299
|
-
|
|
300
|
-
Runner:
|
|
301
|
-
|
|
302
|
-
- use `ttsx` for direct execution.
|
|
303
|
-
- reuse the same `compilerOptions.plugins` contract.
|
|
304
|
-
- keep build-time and runtime transform behavior aligned.
|
|
93
|
+
It is transformed into dedicated JavaScript:
|
|
94
|
+
|
|
95
|
+
> ```js
|
|
96
|
+
> import typia from "typia";
|
|
97
|
+
> import * as __typia_transform__isFormatEmail from "typia/lib/internal/_isFormatEmail";
|
|
98
|
+
> import * as __typia_transform__isFormatUuid from "typia/lib/internal/_isFormatUuid";
|
|
99
|
+
> import * as __typia_transform__isTypeUint32 from "typia/lib/internal/_isTypeUint32";
|
|
100
|
+
> import { v4 } from "uuid";
|
|
101
|
+
>
|
|
102
|
+
> const matched = (() => {
|
|
103
|
+
> const _io0 = (input) =>
|
|
104
|
+
> "string" === typeof input.id &&
|
|
105
|
+
> __typia_transform__isFormatUuid._isFormatUuid(input.id) &&
|
|
106
|
+
> "string" === typeof input.email &&
|
|
107
|
+
> __typia_transform__isFormatEmail._isFormatEmail(input.email) &&
|
|
108
|
+
> "number" === typeof input.age &&
|
|
109
|
+
> __typia_transform__isTypeUint32._isTypeUint32(input.age) &&
|
|
110
|
+
> 19 < input.age &&
|
|
111
|
+
> input.age <= 100;
|
|
112
|
+
> return (input) => "object" === typeof input && null !== input && _io0(input);
|
|
113
|
+
> })()({
|
|
114
|
+
> id: v4(),
|
|
115
|
+
> email: "samchon.github@gmai19l.com",
|
|
116
|
+
> age: 30,
|
|
117
|
+
> });
|
|
118
|
+
> console.log(matched); // true
|
|
119
|
+
> ```
|
|
120
|
+
|
|
121
|
+
`ttsc` runs this transform during build, and `ttsx` runs through the same transform path during execution.
|
|
305
122
|
|
|
306
123
|
## References
|
|
307
124
|
|
|
308
125
|
- TypeScript runners: [`ts-node`](https://github.com/TypeStrong/ts-node) and [`tsx`](https://github.com/privatenumber/tsx)
|
|
309
126
|
- Transformer tooling: [`ttypescript`](https://github.com/cevek/ttypescript) and [`ts-patch`](https://github.com/nonara/ts-patch)
|
|
310
|
-
- Inspired by: [`typical`](https://github.com/
|
|
127
|
+
- Inspired by: [`typical`](https://github.com/elliots/typical) and [`tsgonest`](https://github.com/tsgonest/tsgonest)
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -31,4 +31,5 @@ __exportStar(require("./platform"), exports);
|
|
|
31
31
|
__exportStar(require("./api"), exports);
|
|
32
32
|
__exportStar(require("./project"), exports);
|
|
33
33
|
__exportStar(require("./plugin"), exports);
|
|
34
|
+
__exportStar(require("./runner/register"), exports);
|
|
34
35
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;;;;;;;;;;;;;;;AAEH,6CAA2B;AAC3B,wCAAsB;AACtB,4CAA0B;AAC1B,2CAAyB"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;;;;;;;;;;;;;;;AAEH,6CAA2B;AAC3B,wCAAsB;AACtB,4CAA0B;AAC1B,2CAAyB;AACzB,oDAAkC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function main(argv?: readonly string[]): number;
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.main = main;
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
const Module = require("node:module");
|
|
39
|
+
const node_child_process_1 = require("node:child_process");
|
|
40
|
+
const path = __importStar(require("node:path"));
|
|
41
|
+
const api_1 = require("../api");
|
|
42
|
+
const register_1 = require("./register");
|
|
43
|
+
function main(argv = process.argv.slice(2)) {
|
|
44
|
+
try {
|
|
45
|
+
return run(argv);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
process.stderr.write(`${formatError(error)}\n`);
|
|
49
|
+
return 2;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function run(argv) {
|
|
53
|
+
const parsed = parseCLI(argv);
|
|
54
|
+
if (parsed === "help") {
|
|
55
|
+
printHelp();
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
if (parsed === "version") {
|
|
59
|
+
process.stdout.write(`${(0, api_1.version)().replace(/^ttsc\b/, "ttsx")}\n`);
|
|
60
|
+
return 0;
|
|
61
|
+
}
|
|
62
|
+
const cwd = path.resolve(parsed.cwd ?? process.cwd());
|
|
63
|
+
const entry = path.resolve(cwd, parsed.entry);
|
|
64
|
+
if (!fs.existsSync(entry)) {
|
|
65
|
+
process.stderr.write(`ttsx: entry not found: ${entry}\n`);
|
|
66
|
+
return 2;
|
|
67
|
+
}
|
|
68
|
+
const prepared = (0, register_1.prepareExecution)(entry, {
|
|
69
|
+
binary: parsed.binary,
|
|
70
|
+
cacheDir: parsed.cacheDir,
|
|
71
|
+
cwd,
|
|
72
|
+
env: parsed.env,
|
|
73
|
+
extensions: parsed.extensions,
|
|
74
|
+
project: parsed.project,
|
|
75
|
+
});
|
|
76
|
+
if (prepared.moduleKind === "esm") {
|
|
77
|
+
return runEsmEntry(parsed, prepared, cwd);
|
|
78
|
+
}
|
|
79
|
+
const unregister = (0, register_1.register)({
|
|
80
|
+
binary: parsed.binary,
|
|
81
|
+
cacheDir: parsed.cacheDir,
|
|
82
|
+
cwd,
|
|
83
|
+
env: parsed.env,
|
|
84
|
+
extensions: parsed.extensions,
|
|
85
|
+
project: parsed.project,
|
|
86
|
+
});
|
|
87
|
+
process.once("exit", unregister);
|
|
88
|
+
for (const preload of parsed.preload) {
|
|
89
|
+
require(resolvePreload(cwd, preload));
|
|
90
|
+
}
|
|
91
|
+
process.argv = [process.execPath, entry, ...parsed.passthrough];
|
|
92
|
+
Module.runMain();
|
|
93
|
+
return 0;
|
|
94
|
+
}
|
|
95
|
+
function formatError(error) {
|
|
96
|
+
if (error instanceof Error) {
|
|
97
|
+
return error.message;
|
|
98
|
+
}
|
|
99
|
+
return String(error);
|
|
100
|
+
}
|
|
101
|
+
function parseCLI(argv) {
|
|
102
|
+
const preload = [];
|
|
103
|
+
const passthroughIndex = argv.indexOf("--");
|
|
104
|
+
const head = passthroughIndex === -1 ? [...argv] : [...argv.slice(0, passthroughIndex)];
|
|
105
|
+
const passthrough = passthroughIndex === -1 ? [] : [...argv.slice(passthroughIndex + 1)];
|
|
106
|
+
let binary;
|
|
107
|
+
let cacheDir;
|
|
108
|
+
let cwd;
|
|
109
|
+
let entry;
|
|
110
|
+
let project;
|
|
111
|
+
while (head.length !== 0) {
|
|
112
|
+
const current = head.shift();
|
|
113
|
+
if (entry) {
|
|
114
|
+
passthrough.push(current, ...head);
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
switch (current) {
|
|
118
|
+
case "-h":
|
|
119
|
+
case "--help":
|
|
120
|
+
return "help";
|
|
121
|
+
case "-v":
|
|
122
|
+
case "--version":
|
|
123
|
+
return "version";
|
|
124
|
+
case "-P":
|
|
125
|
+
case "--project":
|
|
126
|
+
project = takeValue(current, head);
|
|
127
|
+
break;
|
|
128
|
+
case "--cwd":
|
|
129
|
+
cwd = takeValue(current, head);
|
|
130
|
+
break;
|
|
131
|
+
case "--cache-dir":
|
|
132
|
+
cacheDir = takeValue(current, head);
|
|
133
|
+
break;
|
|
134
|
+
case "-r":
|
|
135
|
+
case "--require":
|
|
136
|
+
preload.push(takeValue(current, head));
|
|
137
|
+
break;
|
|
138
|
+
case "--binary":
|
|
139
|
+
binary = takeValue(current, head);
|
|
140
|
+
break;
|
|
141
|
+
default:
|
|
142
|
+
if (current.startsWith("--project=")) {
|
|
143
|
+
project = current.slice("--project=".length);
|
|
144
|
+
}
|
|
145
|
+
else if (current.startsWith("--cwd=")) {
|
|
146
|
+
cwd = current.slice("--cwd=".length);
|
|
147
|
+
}
|
|
148
|
+
else if (current.startsWith("--cache-dir=")) {
|
|
149
|
+
cacheDir = current.slice("--cache-dir=".length);
|
|
150
|
+
}
|
|
151
|
+
else if (current.startsWith("--binary=")) {
|
|
152
|
+
binary = current.slice("--binary=".length);
|
|
153
|
+
}
|
|
154
|
+
else if (current.startsWith("-")) {
|
|
155
|
+
throw new Error(`ttsx: unknown option ${current}`);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
entry = current;
|
|
159
|
+
}
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (!entry) {
|
|
164
|
+
throw new Error("ttsx: entry file is required");
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
binary,
|
|
168
|
+
cacheDir,
|
|
169
|
+
cwd,
|
|
170
|
+
entry,
|
|
171
|
+
passthrough,
|
|
172
|
+
preload,
|
|
173
|
+
project,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function printHelp() {
|
|
177
|
+
process.stdout.write([
|
|
178
|
+
"ttsx — TypeScript runner provided by ttsc.",
|
|
179
|
+
"",
|
|
180
|
+
"Usage:",
|
|
181
|
+
" ttsx [options] <entry.ts> [-- <argv...>]",
|
|
182
|
+
"",
|
|
183
|
+
"Options:",
|
|
184
|
+
" -P, --project <file> Use an explicit tsconfig.json",
|
|
185
|
+
" --cwd <dir> Resolve entry/project relative to this directory",
|
|
186
|
+
" --cache-dir <dir> Override the compiled JS cache directory",
|
|
187
|
+
" --binary <path> Use an explicit ttsc native binary",
|
|
188
|
+
" -r, --require <file> Preload a module before the entrypoint",
|
|
189
|
+
" -h, --help Show this help",
|
|
190
|
+
" -v, --version Print the runner version",
|
|
191
|
+
"",
|
|
192
|
+
"Runtime:",
|
|
193
|
+
" CommonJS projects execute in-process via the require hook.",
|
|
194
|
+
" ESM projects execute the cached emitted entry in a child Node process.",
|
|
195
|
+
"",
|
|
196
|
+
"Examples:",
|
|
197
|
+
" ttsx src/index.ts",
|
|
198
|
+
" ttsx --project tsconfig.json src/index.ts -- --port 3000",
|
|
199
|
+
].join("\n"));
|
|
200
|
+
process.stdout.write("\n");
|
|
201
|
+
}
|
|
202
|
+
function resolvePreload(cwd, preload) {
|
|
203
|
+
if (preload.startsWith(".") || preload.startsWith("/") || preload.includes(path.sep)) {
|
|
204
|
+
return path.resolve(cwd, preload);
|
|
205
|
+
}
|
|
206
|
+
return preload;
|
|
207
|
+
}
|
|
208
|
+
function takeValue(flag, rest) {
|
|
209
|
+
const value = rest.shift();
|
|
210
|
+
if (!value) {
|
|
211
|
+
throw new Error(`ttsx: ${flag} requires a value`);
|
|
212
|
+
}
|
|
213
|
+
return value;
|
|
214
|
+
}
|
|
215
|
+
function runEsmEntry(parsed, execution, cwd) {
|
|
216
|
+
fs.mkdirSync(execution.emitDir, { recursive: true });
|
|
217
|
+
rewriteEsmSpecifiers(execution.emitDir);
|
|
218
|
+
fs.writeFileSync(path.join(execution.emitDir, "package.json"), JSON.stringify({ type: "module" }), "utf8");
|
|
219
|
+
const args = [
|
|
220
|
+
...parsed.preload.flatMap((preload) => ["-r", resolvePreload(cwd, preload)]),
|
|
221
|
+
execution.entryFile,
|
|
222
|
+
...parsed.passthrough,
|
|
223
|
+
];
|
|
224
|
+
const result = (0, node_child_process_1.spawnSync)(process.execPath, args, {
|
|
225
|
+
stdio: "inherit",
|
|
226
|
+
env: process.env,
|
|
227
|
+
windowsHide: true,
|
|
228
|
+
});
|
|
229
|
+
if (result.error) {
|
|
230
|
+
process.stderr.write(`${result.error.message}\n`);
|
|
231
|
+
return 1;
|
|
232
|
+
}
|
|
233
|
+
return result.status ?? 1;
|
|
234
|
+
}
|
|
235
|
+
function rewriteEsmSpecifiers(root) {
|
|
236
|
+
const stack = [root];
|
|
237
|
+
while (stack.length !== 0) {
|
|
238
|
+
const current = stack.pop();
|
|
239
|
+
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
240
|
+
const next = path.join(current, entry.name);
|
|
241
|
+
if (entry.isDirectory()) {
|
|
242
|
+
stack.push(next);
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
if (!entry.isFile() || !isJavaScriptOutput(next)) {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
const before = fs.readFileSync(next, "utf8");
|
|
249
|
+
const after = before
|
|
250
|
+
.replace(/\bfrom\s+(['"])(\.[^'"]+)\1/g, (_, quote, specifier) => `from ${quote}${withResolvableExtension(next, specifier)}${quote}`)
|
|
251
|
+
.replace(/\bimport\(\s*(['"])(\.[^'"]+)\1\s*\)/g, (_, quote, specifier) => `import(${quote}${withResolvableExtension(next, specifier)}${quote})`);
|
|
252
|
+
if (after !== before) {
|
|
253
|
+
fs.writeFileSync(next, after, "utf8");
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
function withResolvableExtension(fromFile, specifier) {
|
|
259
|
+
if (!specifier.startsWith(".")) {
|
|
260
|
+
return specifier;
|
|
261
|
+
}
|
|
262
|
+
if (/\.(?:[cm]?js|json|node)$/i.test(specifier)) {
|
|
263
|
+
return specifier;
|
|
264
|
+
}
|
|
265
|
+
const [pathname, suffix = ""] = splitSpecifierSuffix(specifier);
|
|
266
|
+
const fromDir = path.dirname(fromFile);
|
|
267
|
+
for (const extension of [".js", ".mjs", ".cjs"]) {
|
|
268
|
+
if (fs.existsSync(path.resolve(fromDir, pathname + extension))) {
|
|
269
|
+
return pathname + extension + suffix;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return `${specifier}.js`;
|
|
273
|
+
}
|
|
274
|
+
function splitSpecifierSuffix(specifier) {
|
|
275
|
+
const index = specifier.search(/[?#]/);
|
|
276
|
+
if (index === -1) {
|
|
277
|
+
return [specifier];
|
|
278
|
+
}
|
|
279
|
+
return [specifier.slice(0, index), specifier.slice(index)];
|
|
280
|
+
}
|
|
281
|
+
function isJavaScriptOutput(filename) {
|
|
282
|
+
return /\.(?:[cm]?js)$/i.test(filename);
|
|
283
|
+
}
|
|
284
|
+
//# sourceMappingURL=cli.js.map
|