tsnite 0.1.4 → 0.2.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/README.md +32 -80
- package/dist/cli.js +17 -10
- package/dist/loader.js +23 -32
- package/dist/util.js +33 -0
- package/package.json +7 -9
package/README.md
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
<h1 align="center">
|
|
2
|
+
<br>
|
|
3
|
+
<img alt="tsnite" src="https://github.com/luas10c/tsnite/blob/main/tsnite.png?raw=true">
|
|
4
|
+
<br><br>
|
|
5
|
+
<a href="https://npm.im/tsnite"><img src="https://badgen.net/npm/v/tsnite"></a>
|
|
6
|
+
<a href="https://npm.im/tsnite"><img src="https://badgen.net/npm/dm/tsnite"></a>
|
|
7
|
+
<a href="https://npm.im/tsnite"><img src="https://img.shields.io/badge/ESLint-3A33D1?logo=eslint" alt="eslint"></a>
|
|
8
|
+
<a href="https://npm.im/tsnite"><img src="https://img.shields.io/badge/Prettier-21323b?logo=prettier&logoColor=ffffff" alt="prettier"></a>
|
|
9
|
+
<a href="https://npm.im/tsnite"><img src="https://img.shields.io/github/license/luas10c/tsnite" alt="github license"></a>
|
|
10
|
+
</h1>
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
TypeScript at full throttle—fast, safe, unstoppable. 🚀
|
|
14
|
+
<br><br>
|
|
15
|
+
<a href="https://github.com/luas10c/tsnite">Documentation</a> | <a href="https://github.com/luas10c/tsnite">Getting started →</a>
|
|
16
|
+
</p>
|
|
9
17
|
|
|
10
18
|
## Install
|
|
11
19
|
|
|
@@ -13,7 +21,15 @@ TypeScript runner for Node.js with watch mode, `tsconfig` path alias support, an
|
|
|
13
21
|
npm install --save-dev tsnite
|
|
14
22
|
```
|
|
15
23
|
|
|
16
|
-
##
|
|
24
|
+
## Features
|
|
25
|
+
|
|
26
|
+
- ESM-friendly runtime
|
|
27
|
+
- Watch mode with automatic restart
|
|
28
|
+
- Supports `compilerOptions.paths`
|
|
29
|
+
- Resolves extensionless TypeScript imports
|
|
30
|
+
- Decorator support
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
17
33
|
|
|
18
34
|
Run a TypeScript entry file:
|
|
19
35
|
|
|
@@ -44,16 +60,6 @@ Use in `package.json`:
|
|
|
44
60
|
}
|
|
45
61
|
```
|
|
46
62
|
|
|
47
|
-
## Features
|
|
48
|
-
|
|
49
|
-
- Run `.ts`, `.tsx`, `.mts`, and `.cts` files directly in Node.js
|
|
50
|
-
- Watch mode with automatic restart
|
|
51
|
-
- Reads the current project's `tsconfig.json`
|
|
52
|
-
- Resolves `compilerOptions.paths`
|
|
53
|
-
- Resolves extensionless TypeScript imports
|
|
54
|
-
- ESM-friendly runtime
|
|
55
|
-
- Decorator support in transpilation
|
|
56
|
-
|
|
57
63
|
## Import Resolution
|
|
58
64
|
|
|
59
65
|
`tsnite` resolves TypeScript files without requiring explicit extensions.
|
|
@@ -78,29 +84,6 @@ It will try these TypeScript candidates:
|
|
|
78
84
|
|
|
79
85
|
Explicit JavaScript imports such as `.js`, `.mjs`, and `.cjs` are delegated to Node.js.
|
|
80
86
|
|
|
81
|
-
## `tsconfig` Paths
|
|
82
|
-
|
|
83
|
-
`tsnite` supports `compilerOptions.paths` aliases.
|
|
84
|
-
|
|
85
|
-
```json
|
|
86
|
-
{
|
|
87
|
-
"compilerOptions": {
|
|
88
|
-
"baseUrl": ".",
|
|
89
|
-
"paths": {
|
|
90
|
-
"#/*": ["src/*"]
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
Then this works without an extension:
|
|
97
|
-
|
|
98
|
-
```ts
|
|
99
|
-
import { getMetadata } from '#/common/metadata'
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
If `baseUrl` is not defined, `tsnite` uses the project root by default.
|
|
103
|
-
|
|
104
87
|
## Watch Mode
|
|
105
88
|
|
|
106
89
|
Customize watched paths and extensions:
|
|
@@ -123,45 +106,14 @@ Defaults:
|
|
|
123
106
|
- ext: `ts,tsx,js,jsx,json`
|
|
124
107
|
- source root: `.`
|
|
125
108
|
|
|
126
|
-
##
|
|
127
|
-
|
|
128
|
-
`tsconfig.json`
|
|
129
|
-
|
|
130
|
-
```json
|
|
131
|
-
{
|
|
132
|
-
"compilerOptions": {
|
|
133
|
-
"target": "es2024",
|
|
134
|
-
"module": "es2022",
|
|
135
|
-
"moduleResolution": "bundler",
|
|
136
|
-
"baseUrl": ".",
|
|
137
|
-
"paths": {
|
|
138
|
-
"#/*": ["src/*"]
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
`src/index.ts`
|
|
145
|
-
|
|
146
|
-
```ts
|
|
147
|
-
import { startServer } from '#/server/start'
|
|
148
|
-
|
|
149
|
-
await startServer()
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
Run it:
|
|
153
|
-
|
|
154
|
-
```bash
|
|
155
|
-
npx tsnite src/index.ts
|
|
156
|
-
```
|
|
109
|
+
## Support
|
|
157
110
|
|
|
158
|
-
|
|
111
|
+
Enjoying this tool? Consider supporting the project.
|
|
159
112
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
113
|
+
<p>
|
|
114
|
+
<a href="https://buymeacoffee.com/luas10c" target="_blank" rel="noopener noreferrer"><img src="https://github.com/luas10c/tsnite/blob/main/buymeacoffe.png?raw=true" alt="Buy me a coffee" width="180" /></a>
|
|
115
|
+
</p>
|
|
163
116
|
|
|
164
|
-
##
|
|
117
|
+
## License
|
|
165
118
|
|
|
166
|
-
|
|
167
|
-
- repository: <https://github.com/luas10c/tsnite>
|
|
119
|
+
[MIT](LICENSE)
|
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,7 @@ import { fork } from 'node:child_process';
|
|
|
6
6
|
import { watch } from 'chokidar';
|
|
7
7
|
import { name, description, version } from './metadata.js';
|
|
8
8
|
import { clearResolveCache, invalidateFileCaches } from './cache.js';
|
|
9
|
-
import { debounce, yellow } from './util.js';
|
|
9
|
+
import { debounce, cyan, gray, red, yellow } from './util.js';
|
|
10
10
|
const DEFAULT_INCLUDE_PATHS = ['.'];
|
|
11
11
|
const DEFAULT_EXCLUDE_PATHS = ['dist', 'build', 'coverage'];
|
|
12
12
|
const DEFAULT_WATCH_EXTENSIONS = ['ts', 'tsx', 'js', 'jsx', 'json'];
|
|
@@ -20,7 +20,7 @@ program
|
|
|
20
20
|
.version(version, '-v, --version', 'Output the current version')
|
|
21
21
|
.showSuggestionAfterError();
|
|
22
22
|
function cleanup(signal) {
|
|
23
|
-
process.stdout.write(`${yellow(`Received ${signal}. Stopping watcher and child processes
|
|
23
|
+
process.stdout.write(`${yellow(`Received`)} ${cyan(signal)}${yellow('. Stopping watcher and child processes...')}\n`);
|
|
24
24
|
for (const child of children.values()) {
|
|
25
25
|
try {
|
|
26
26
|
child.kill('SIGTERM');
|
|
@@ -37,9 +37,14 @@ process.on('SIGINT', function () {
|
|
|
37
37
|
process.on('SIGTERM', function () {
|
|
38
38
|
cleanup('SIGTERM');
|
|
39
39
|
});
|
|
40
|
-
function spawn(entry, nodeArgs) {
|
|
41
|
-
const
|
|
40
|
+
function spawn(entry, nodeArgs, shouldLogTranspile) {
|
|
41
|
+
const entryPath = isAbsolute(entry) ? entry : resolve(process.cwd(), entry);
|
|
42
|
+
const child = fork(entryPath, {
|
|
42
43
|
stdio: 'inherit',
|
|
44
|
+
env: {
|
|
45
|
+
...process.env,
|
|
46
|
+
TSNITE_LOG_TRANSPILE: shouldLogTranspile ? '1' : '0'
|
|
47
|
+
},
|
|
43
48
|
execArgv: [
|
|
44
49
|
'--enable-source-maps',
|
|
45
50
|
'--no-experimental-strip-types',
|
|
@@ -130,10 +135,11 @@ function createWatchConfig(options) {
|
|
|
130
135
|
};
|
|
131
136
|
}
|
|
132
137
|
async function handler(entry, options, nodeArgs, isWatch) {
|
|
138
|
+
const runtimeEntry = isAbsolute(entry) ? entry : resolve(process.cwd(), entry);
|
|
133
139
|
async function restart(reason) {
|
|
134
140
|
process.stdout.write('\x1Bc');
|
|
135
141
|
if (reason) {
|
|
136
|
-
console.log(
|
|
142
|
+
console.log(gray(reason));
|
|
137
143
|
}
|
|
138
144
|
for (const child of children.values()) {
|
|
139
145
|
try {
|
|
@@ -145,7 +151,7 @@ async function handler(entry, options, nodeArgs, isWatch) {
|
|
|
145
151
|
const exited = await waitForChildExit(child);
|
|
146
152
|
if (exited)
|
|
147
153
|
continue;
|
|
148
|
-
console.log(yellow(
|
|
154
|
+
console.log(`${yellow('restart')} ${gray('process unresponsive, sending')} ${red('SIGKILL')}`);
|
|
149
155
|
try {
|
|
150
156
|
child.kill('SIGKILL');
|
|
151
157
|
}
|
|
@@ -154,14 +160,14 @@ async function handler(entry, options, nodeArgs, isWatch) {
|
|
|
154
160
|
}
|
|
155
161
|
}
|
|
156
162
|
clearResolveCache();
|
|
157
|
-
spawn(
|
|
163
|
+
spawn(runtimeEntry, nodeArgs, isWatch);
|
|
158
164
|
}
|
|
159
165
|
const restartDebounced = debounce(restart, WATCH_DEBOUNCE_MS);
|
|
160
166
|
process.stdout.write('\x1Bc');
|
|
161
167
|
if (isWatch) {
|
|
162
168
|
console.log(yellow('Watching for changes...'));
|
|
163
169
|
}
|
|
164
|
-
spawn(
|
|
170
|
+
spawn(runtimeEntry, nodeArgs, isWatch);
|
|
165
171
|
if (!isWatch)
|
|
166
172
|
return;
|
|
167
173
|
const { ignored, paths } = createWatchConfig(options);
|
|
@@ -176,8 +182,9 @@ async function handler(entry, options, nodeArgs, isWatch) {
|
|
|
176
182
|
eventName !== 'unlink') {
|
|
177
183
|
return;
|
|
178
184
|
}
|
|
179
|
-
|
|
180
|
-
|
|
185
|
+
const absoluteChangedPath = isAbsolute(changedPath) ? changedPath : (resolve(process.cwd(), changedPath));
|
|
186
|
+
await invalidateFileCaches(absoluteChangedPath);
|
|
187
|
+
restartDebounced();
|
|
181
188
|
});
|
|
182
189
|
}
|
|
183
190
|
function parseCsv(value) {
|
package/dist/loader.js
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { transform } from 'oxc-transform';
|
|
2
2
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
3
3
|
import { readFile, stat, writeFile } from 'node:fs/promises';
|
|
4
|
-
import {
|
|
4
|
+
import { writeSync } from 'node:fs';
|
|
5
|
+
import { extname, join, dirname, relative } from 'node:path';
|
|
5
6
|
import { createHash } from 'node:crypto';
|
|
6
7
|
import { parse } from './parse.js';
|
|
7
8
|
import { ensureTranspileCacheDir, existsWithCache, getTranspileCacheFile, resolveCache } from './cache.js';
|
|
9
|
+
import { gray, green, yellow } from './util.js';
|
|
8
10
|
const tsconfigCache = { paths: null, baseUrl: null };
|
|
9
11
|
const transpileCache = new Map();
|
|
10
12
|
const MAX_TRANSPILE_CACHE_ENTRIES = 256;
|
|
11
13
|
const TS_EXTENSIONS = ['.cts', '.mts', '.tsx', '.ts'];
|
|
14
|
+
const SHOULD_LOG_TRANSPILE = process.env.TSNITE_LOG_TRANSPILE === '1';
|
|
12
15
|
function hasTypeScriptExtension(value) {
|
|
13
16
|
return TS_EXTENSIONS.some((extension) => value.endsWith(extension));
|
|
14
17
|
}
|
|
@@ -221,38 +224,26 @@ export async function load(url, ctx, next) {
|
|
|
221
224
|
if (cachedCode !== null) {
|
|
222
225
|
return { format: 'module', source: cachedCode, shortCircuit: true };
|
|
223
226
|
}
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
tsx: url.endsWith('.tsx'),
|
|
231
|
-
decorators: true
|
|
232
|
-
},
|
|
233
|
-
target: 'es2022',
|
|
234
|
-
keepClassNames: true,
|
|
235
|
-
experimental: {
|
|
236
|
-
emitAssertForImportAttributes: true
|
|
237
|
-
},
|
|
238
|
-
transform: {
|
|
239
|
-
decoratorMetadata: true,
|
|
240
|
-
legacyDecorator: true,
|
|
241
|
-
react: {
|
|
242
|
-
runtime: 'automatic',
|
|
243
|
-
development: true
|
|
244
|
-
}
|
|
245
|
-
},
|
|
246
|
-
paths: paths ?? {}
|
|
227
|
+
const source = await readFile(filename, 'utf8');
|
|
228
|
+
const transpileStart = Date.now();
|
|
229
|
+
const { code } = await transform(filename, source, {
|
|
230
|
+
jsx: {
|
|
231
|
+
runtime: 'automatic',
|
|
232
|
+
development: true
|
|
247
233
|
},
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
234
|
+
sourcemap: true,
|
|
235
|
+
sourceType: 'module',
|
|
236
|
+
target: 'es2024',
|
|
237
|
+
decorator: {
|
|
238
|
+
emitDecoratorMetadata: true,
|
|
239
|
+
legacy: true
|
|
240
|
+
}
|
|
255
241
|
});
|
|
242
|
+
if (SHOULD_LOG_TRANSPILE) {
|
|
243
|
+
const transpileTimeMs = Date.now() - transpileStart;
|
|
244
|
+
const displayPath = relative(process.cwd(), filename);
|
|
245
|
+
writeSync(1, `${gray('Transpiled')} ${yellow(displayPath)} ${gray('in')} ${green(`${transpileTimeMs}ms`)}\n`);
|
|
246
|
+
}
|
|
256
247
|
await writeCachedTranspile(filename, {
|
|
257
248
|
code,
|
|
258
249
|
mtimeMs: fileStats.mtimeMs,
|
package/dist/util.js
CHANGED
|
@@ -9,6 +9,39 @@ export function debounce(callback, waitMs) {
|
|
|
9
9
|
function color(code, value) {
|
|
10
10
|
return `\u001B[${code}m${value}\u001B[0m`;
|
|
11
11
|
}
|
|
12
|
+
function style(codes, value) {
|
|
13
|
+
return `\u001B[${codes.join(';')}m${value}\u001B[0m`;
|
|
14
|
+
}
|
|
12
15
|
export function yellow(value) {
|
|
13
16
|
return color(33, value);
|
|
14
17
|
}
|
|
18
|
+
export function purple(value) {
|
|
19
|
+
return color(95, value);
|
|
20
|
+
}
|
|
21
|
+
export function red(value) {
|
|
22
|
+
return color(31, value);
|
|
23
|
+
}
|
|
24
|
+
export function blue(value) {
|
|
25
|
+
return color(34, value);
|
|
26
|
+
}
|
|
27
|
+
export function white(value) {
|
|
28
|
+
return color(97, value);
|
|
29
|
+
}
|
|
30
|
+
export function pink(value) {
|
|
31
|
+
return color(95, value);
|
|
32
|
+
}
|
|
33
|
+
export function cyan(value) {
|
|
34
|
+
return color(96, value);
|
|
35
|
+
}
|
|
36
|
+
export function green(value) {
|
|
37
|
+
return color(32, value);
|
|
38
|
+
}
|
|
39
|
+
export function gray(value) {
|
|
40
|
+
return color(90, value);
|
|
41
|
+
}
|
|
42
|
+
export function bold(value) {
|
|
43
|
+
return style([1], value);
|
|
44
|
+
}
|
|
45
|
+
export function accent(value) {
|
|
46
|
+
return style([1, 36], value);
|
|
47
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tsnite",
|
|
3
3
|
"description": "TypeScript at full throttle—fast, safe, unstoppable. 🚀",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
7
7
|
"esm",
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
},
|
|
21
21
|
"type": "module",
|
|
22
22
|
"scripts": {
|
|
23
|
-
"prebuild": "rimraf dist",
|
|
24
23
|
"build": "tsc -p tsconfig.build.json",
|
|
25
24
|
"postbuild": "tsc-alias -f -p tsconfig.build.json",
|
|
26
25
|
"lint": "eslint . --cache",
|
|
@@ -32,9 +31,9 @@
|
|
|
32
31
|
"prepare": "husky"
|
|
33
32
|
},
|
|
34
33
|
"dependencies": {
|
|
35
|
-
"@swc/core": "^1.15.24",
|
|
36
34
|
"chokidar": "^5.0.0",
|
|
37
|
-
"commander": "^14.0.3"
|
|
35
|
+
"commander": "^14.0.3",
|
|
36
|
+
"oxc-transform": "^0.127.0"
|
|
38
37
|
},
|
|
39
38
|
"devDependencies": {
|
|
40
39
|
"@commitlint/cli": "^20.5.0",
|
|
@@ -42,7 +41,7 @@
|
|
|
42
41
|
"@eslint/js": "^10.0.1",
|
|
43
42
|
"@jest/globals": "^30.3.0",
|
|
44
43
|
"@swc/jest": "^0.2.39",
|
|
45
|
-
"eslint": "^10.2.
|
|
44
|
+
"eslint": "^10.2.1",
|
|
46
45
|
"eslint-plugin-jest": "^29.15.2",
|
|
47
46
|
"eslint-plugin-prettier": "^5.5.5",
|
|
48
47
|
"globals": "^17.5.0",
|
|
@@ -50,11 +49,10 @@
|
|
|
50
49
|
"jest": "^30.3.0",
|
|
51
50
|
"jest-environment-node": "^30.3.0",
|
|
52
51
|
"lint-staged": "^16.4.0",
|
|
53
|
-
"prettier": "^3.8.
|
|
54
|
-
"rimraf": "^6.1.3",
|
|
52
|
+
"prettier": "^3.8.3",
|
|
55
53
|
"tsc-alias": "^1.8.16",
|
|
56
|
-
"typescript": "^6.0.
|
|
57
|
-
"typescript-eslint": "^8.
|
|
54
|
+
"typescript": "^6.0.3",
|
|
55
|
+
"typescript-eslint": "^8.59.0"
|
|
58
56
|
},
|
|
59
57
|
"engines": {
|
|
60
58
|
"node": "^20.9.0 || ^22.11.0 || ^24.11.0"
|