🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

tsxx

Package Overview
Dependencies
Maintainers
1
Versions
20
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

tsxx - npm Package Compare versions

Comparing version
1.0.18
to
1.1.0
+77
dist/hmr-hook.cjs
/* eslint-disable unused-imports/no-unused-vars */
const net = require('node:net')
const process = require('node:process')
// noinspection JSUnusedLocalSymbols
const http = require('node:http')
/**
* @type {http.Server[]}
*/
const services = []
function hookNetServer() {
const oldCreateServer = net.createServer
net.createServer = (...args) => {
const server = oldCreateServer.call(this, ...args)
services.push(server)
return server
}
}
function hookHttpServer() {
const http = require('node:http')
const oldCreateServer = http.createServer
http.createServer = (...args) => {
const service = oldCreateServer.call(this, ...args)
services.push(service)
return service
}
}
async function closeServices() {
let count = 0
process.on('uncaughtException', (error) => {
console.error('Uncaught Exception:', error)
})
for (const index in services) {
const service = services[index]
await new Promise((resolve, reject) => {
try {
// noinspection JSUnresolvedReference
service.closeAllConnections()
// noinspection JSUnresolvedReference
service.closeIdleConnections()
// noinspection JSUnresolvedReference
service.close((err) => {
if (err) {
reject(err)
return
}
count++
resolve()
})
}
catch (e) {
console.error(e)
}
resolve()
}).catch((e) => {
console.error(e)
})
}
services.splice(0, services.length)
return count
}
function hookServices() {
hookNetServer()
hookHttpServer()
}
async function unblocks() {
return await closeServices()
}
module.exports.hookServices = hookServices
module.exports.unblocks = unblocks
const http = require('node:http')
module.exports = http
const net = require('node:net')
module.exports = net
const url = require('node:url')
function getHookNetUrl() {
return url.pathToFileURL(require.resolve('./hook-net.cjs')).toString()
}
function getHookHttpUrl() {
return url.pathToFileURL(require.resolve('./hook-http.cjs')).toString()
}
module.exports.getHookNetUrl = getHookNetUrl
module.exports.getHookHttpUrl = getHookHttpUrl
const url = require('node:url')
const path = require('node:path')
const loaders = [
['--require', './hmr-proxy.cjs'],
['--import', './loaders/loader-block.mjs'],
]
function getLoaderUrl(includeType, pathname) {
if (includeType === '--require')
return path.join(__dirname, pathname)
else
return url.pathToFileURL(require.resolve(pathname)).toString()
}
function getLoaderArgs() {
const args = []
for (const loader of loaders) {
const [includeType, pathname] = loader
args.push(includeType, getLoaderUrl(includeType, pathname))
}
return args
}
function getSpawnArgs(oldArgs) {
const newArgs = [...oldArgs]
newArgs.splice(0, 0, ...getLoaderArgs())
return newArgs
}
class Debounce {
constructor(func, delay) {
this.func = func
this.delay = delay
this.timeout = null
}
// noinspection JSUnusedGlobalSymbols
call(...args) {
if (this.timeout)
clearTimeout(this.timeout)
this.timeout = setTimeout(() => {
this.func(...args)
}, this.delay)
}
}
module.exports.Debounce = Debounce
module.exports.getSpawnArgs = getSpawnArgs
module.exports.getLoaderArgs = getLoaderArgs
+10
-8
#!/usr/bin/env node
const childProcess = require('child_process');
const oldSpawn = childProcess.spawn;
const childProcess = require('node:child_process')
const oldSpawn = childProcess.spawn
// noinspection JSValidateTypes

@@ -9,11 +10,12 @@ childProcess.spawn = (command, args, options) => {

*/
const newArgs = require('./util.js').getSpawnArgs(args);
const result = oldSpawn.call(this, command, newArgs, options);
const newArgs = require('./util.cjs').getSpawnArgs(args)
const result = oldSpawn.call(this, command, newArgs, options)
/**
* After launching `spawn` in the TSX, there is no need to hook again. To avoid any adverse effects, revert the `spawn`.
*/
childProcess.spawn = oldSpawn;
return result;
};
childProcess.spawn = oldSpawn
return result
}
import('tsx/cli');
// noinspection NpmUsedModulesInstalled
import('tsx/cli')

@@ -0,77 +1,82 @@

const process = require('node:process')
/**
* @type {string}
*/
const pathname = process.argv.slice(1)[0];
const pathname = process.argv.slice(1)[0]
/**
* @type {{[filename: string]: string[]}}
*/
const relationModuleFilenames = {};
const relationModuleFilenames = {}
/**
* @type {string[]}
*/
const aliveFilenames = [pathname];
const aliveFilenames = [pathname]
const Module = require('node:module');
const Module = require('node:module')
// noinspection JSUnresolvedReference
const oldLoad = Module._load;
const oldLoad = Module._load
Module._load = function (request, parent, isMain) {
const loaded = oldLoad.call(this, request, parent, isMain);
const loaded = oldLoad.call(this, request, parent, isMain)
if (parent) {
const { filename, children } = parent;
if (!aliveFilenames.includes(filename)) {
aliveFilenames.push(filename);
}
const { filename, children } = parent
if (!aliveFilenames.includes(filename))
aliveFilenames.push(filename)
/**
* @type {string[]}
*/
const newFilenames = children.map(child => {
if (!aliveFilenames.includes(child.filename)) {
aliveFilenames.push(child.filename);
}
return child.filename;
});
const newFilenames = children.map((child) => {
if (!aliveFilenames.includes(child.filename))
aliveFilenames.push(child.filename)
return child.filename
})
if (!relationModuleFilenames[filename]) {
relationModuleFilenames[filename] = newFilenames;
} else {
relationModuleFilenames[filename] = newFilenames
}
else {
/**
* @type {string[]}
*/
const oldFilenames = relationModuleFilenames[filename];
relationModuleFilenames[filename] = [...new Set([...newFilenames, ...oldFilenames])];
const oldFilenames = relationModuleFilenames[filename]
relationModuleFilenames[filename] = [...new Set([...newFilenames, ...oldFilenames])]
}
}
return loaded;
};
return loaded
}
const cleanModuleCache = filename => {
function cleanModuleCache(filename) {
// noinspection JSUnresolvedReference
delete Module._cache[filename];
delete relationModuleFilenames[filename];
const aliveIndex = aliveFilenames.indexOf(filename);
if (aliveIndex !== -1) {
aliveFilenames.splice(aliveIndex, 1);
}
delete Module._cache[filename]
delete relationModuleFilenames[filename]
const aliveIndex = aliveFilenames.indexOf(filename)
if (aliveIndex !== -1)
aliveFilenames.splice(aliveIndex, 1)
for (const parentFilename in relationModuleFilenames) {
const developFilenames = relationModuleFilenames[parentFilename];
if (developFilenames.includes(filename)) {
cleanModuleCache(parentFilename);
}
const developFilenames = relationModuleFilenames[parentFilename]
if (developFilenames.includes(filename))
cleanModuleCache(parentFilename)
}
};
}
const { Debounce } = require('./util.js');
const { hookServices, unblocks } = require('./hmr-hook.js');
hookServices();
const debouncedHmrFunc = new Debounce(freshFilename => {
const chokidar = require('chokidar')
const { Debounce } = require('./util.cjs')
const { hookServices, unblocks } = require('./hmr-hook.cjs')
hookServices()
const debouncedHmrFunc = new Debounce((freshFilename) => {
unblocks().then(() => {
cleanModuleCache(freshFilename);
cleanModuleCache(freshFilename)
try {
aliveFilenames.push(pathname);
require(pathname);
} catch (e) {
console.error(e);
aliveFilenames.push(pathname)
require(pathname)
}
});
}, 200);
catch (e) {
console.error(e)
}
})
}, 200)

@@ -81,8 +86,6 @@ /**

*/
const chokidar = require('chokidar');
const chokidarWatcher = chokidar.watch(process.cwd(), { ignored: /node_modules/ });
chokidarWatcher.on('change', freshFilename => {
if (aliveFilenames.includes(freshFilename)) {
debouncedHmrFunc.call(freshFilename);
}
});
const chokidarWatcher = chokidar.watch(process.cwd(), { ignored: /node_modules/ })
chokidarWatcher.on('change', (freshFilename) => {
if (aliveFilenames.includes(freshFilename))
debouncedHmrFunc.call(freshFilename)
})

@@ -1,2 +0,2 @@

import { getHookHttpUrl, getHookNetUrl } from './util.js';
import { getHookHttpUrl, getHookNetUrl } from './util.cjs'

@@ -9,13 +9,12 @@ // noinspection JSUnusedGlobalSymbols

*/
let newSpecifier = specifier;
if (newSpecifier.startsWith('node:')) {
newSpecifier = newSpecifier.slice('node:'.length);
}
if (newSpecifier === 'net') {
specifier = getHookNetUrl();
} else if (newSpecifier === 'http') {
specifier = getHookHttpUrl();
}
let newSpecifier = specifier
if (newSpecifier.startsWith('node:'))
newSpecifier = newSpecifier.slice('node:'.length)
if (newSpecifier === 'net')
specifier = getHookNetUrl()
else if (newSpecifier === 'http')
specifier = getHookHttpUrl()
}
return defaultResolve(specifier, context, defaultResolve);
return defaultResolve(specifier, context, defaultResolve)
}
# tsxx <a href="https://npm.im/tsxx"><img src="https://badgen.net/npm/v/tsxx"></a> <a href="https://npm.im/tsxx"><img src="https://badgen.net/npm/dm/tsxx"></a> <a href="https://packagephobia.now.sh/result?p=tsxx"><img src="https://packagephobia.now.sh/badge?p=tsxx"></a>
install: `npm i -g tsxx` or `npm i tsxx`
real node hmr
usage: `tsxx ./some-script.ts`
[read tsx README.md](https://github.com/esbuild-kit/tsx/tree/develop)
---
<div style="color: #e82274; font-size: 1.2rem;">
中文解释:
</div>
global install:
```
npm i -g tsxx
```
tsxx 是基于 tsx 开发的 nodejs runtime hmr。使用方法与 tsx 完全一致。使用时相对于 tsx 默认开启 hmr 功能。帮助你大幅度增速程序重启。
<div style="color: #e82274; font-size: 1.2rem">
en description:
</div>
tsxx is a Node.js runtime HMR (Hot Module Replacement) tool developed based on TSX. Its usage is completely identical to TSX. When using it, HMR functionality is enabled by default compared to TSX, helping you significantly speed up program restarts.
[read tsx README](https://github.com/esbuild-kit/tsx/tree/develop)
now, you can use `tsxx` command in your terminal.
```
tsxx ./src/index.ts
```

@@ -1,4 +0,4 @@

The MIT License (MIT)
MIT License
Copyright (c) 2016 Christian Speckner <cnspeckn@googlemail.com>
Copyright (c) 2022 Anthony Fu <https://github.com/antfu>

@@ -12,4 +12,4 @@ Permission is hereby granted, free of charge, to any person obtaining a copy

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

@@ -21,3 +21,3 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR

LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
{
"name": "tsxx",
"version": "1.0.18",
"description": "tsx + hmr for node js",
"type": "module",
"version": "1.1.0",
"description": "true node hmr",
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
"license": "MIT",
"funding": "https://github.com/sponsors/sia-fl",
"homepage": "https://github.com/sia-fl/tsxx#readme",
"repository": {
"type": "git",
"url": "https://github.com/sia-fl/tsxx"
"url": "git+https://github.com/sia-fl/tsxx.git"
},
"scripts": {
"build": "node build.mjs",
"lint": "prettier --write .",
"test": "vitest"
"bugs": "https://github.com/sia-fl/tsxx/issues",
"keywords": [],
"sideEffects": false,
"bin": {
"tsxx": "./dist/cli.cjs"
},

@@ -17,16 +23,36 @@ "files": [

],
"bin": {
"tsxx": "dist/cli.cjs"
},
"keywords": ["hmr", "node", "tsx", "esbuild", "cjs", "esm"],
"author": "杨伟杰 <sia-fl@outlook.com> (https://github.com/sia-fl)",
"license": "MIT",
"dependencies": {
"chokidar": "^3.5.3",
"tsx": "^3.12.8"
"@types/kill-port": "^2.0.3",
"@types/signale": "^1.4.7",
"chokidar": "^3.6.0",
"kill-port": "^2.0.1",
"signale": "^1.4.0",
"tsx": "^4.7.3"
},
"devDependencies": {
"prettier": "^3.0.3",
"vitest": "^0.34.5"
"@antfu/eslint-config": "^2.14.0",
"@antfu/ni": "^0.21.12",
"@antfu/utils": "^0.7.7",
"@types/node": "^20.12.7",
"eslint": "^9.0.0",
"lint-staged": "^15.2.2",
"pnpm": "^9.0.2",
"rimraf": "^5.0.5",
"simple-git-hooks": "^2.11.1",
"typescript": "^5.4.5",
"vite": "^5.2.9",
"vitest": "^1.5.0"
},
"simple-git-hooks": {
"pre-commit": "pnpm lint-staged"
},
"lint-staged": {
"*": "eslint --fix"
},
"scripts": {
"build": "rimraf dist && node build.mjs",
"lint": "eslint .",
"test": "vitest",
"typecheck": "tsc --noEmit"
}
}
}
# tsxx <a href="https://npm.im/tsxx"><img src="https://badgen.net/npm/v/tsxx"></a> <a href="https://npm.im/tsxx"><img src="https://badgen.net/npm/dm/tsxx"></a> <a href="https://packagephobia.now.sh/result?p=tsxx"><img src="https://packagephobia.now.sh/badge?p=tsxx"></a>
tsxx is a Node.js runtime HMR (Hot Module Replacement) tool developed based on tsx. Its usage is completely identical to tsx. When using it, HMR functionality is enabled by default compared to tsx, helping you significantly speed up program restarts.
real node hmr

@@ -9,7 +9,2 @@ [read tsx README.md](https://github.com/esbuild-kit/tsx/tree/develop)

install to npm devDependencies :
```
npm i -D tsxx
```
global install:

@@ -24,17 +19,1 @@ ```

```
usage:
```json
{
"scripts": {
"dev": "tsxx ./src/index.tsx"
}
}
```
# warning ⚠️⚠️⚠️⚠️
not set `"type": "module"` in `package.json`, if you use `.js`, change file extension to `.mjs` or `.cjs`
.
determining the file type helps tsx better address import issues.
const net = require('net');
const http = require('http');
/**
* @type {http.Server[]}
*/
const services = [];
const hookNetServer = () => {
const oldCreateServer = net.createServer;
net.createServer = (...args) => {
const server = oldCreateServer.call(this, ...args);
services.push(server);
return server;
};
};
const hookHttpServer = () => {
const http = require('http');
const oldCreateServer = http.createServer;
http.createServer = (...args) => {
const server = oldCreateServer.call(this, ...args);
services.push(server);
return server;
};
};
const closeServices = async () => {
let count = 0;
process.on('uncaughtException', error => {
console.error('Uncaught Exception:', error);
});
for (let index in services) {
const service = services[index];
await new Promise((resolve, reject) => {
try {
// noinspection JSUnresolvedReference
service.closeAllConnections();
// noinspection JSUnresolvedReference
service.closeIdleConnections();
// noinspection JSUnresolvedReference
service.close(err => {
if (err) {
reject(err);
return;
}
count++;
resolve();
});
} catch (e) {
console.error(e);
}
resolve();
}).catch(e => {
console.error(e);
});
}
services.splice(0, services.length);
return count;
};
const hookServices = () => {
hookNetServer();
hookHttpServer();
};
const unblocks = async () => {
return await closeServices();
};
module.exports.hookServices = hookServices;
module.exports.unblocks = unblocks;
const http = require('http');
module.exports = http;
const net = require('net');
module.exports = net;
const url = require('url');
const getHookNetUrl = () => {
return url.pathToFileURL(require.resolve('./hook-net.js')).toString();
};
const getHookHttpUrl = () => {
return url.pathToFileURL(require.resolve('./hook-http.js')).toString();
};
module.exports.getHookNetUrl = getHookNetUrl;
module.exports.getHookHttpUrl = getHookHttpUrl;
const url = require('url');
const path = require('path');
const loaders = [
['--require', './hmr-proxy.cjs'],
['--loader', './loaders/loader-block.mjs']
];
const getLoaderUrl = (includeType, pathname) => {
if (includeType === '--require') {
return path.join(__dirname, pathname);
} else {
return url.pathToFileURL(require.resolve(pathname)).toString();
}
};
const getLoaderArgs = () => {
const args = [];
for (const loader of loaders) {
const [includeType, pathname] = loader;
args.push(includeType, getLoaderUrl(includeType, pathname));
}
return args;
};
const getSpawnArgs = oldArgs => {
const newArgs = [...oldArgs];
newArgs.splice(0, 0, ...getLoaderArgs());
return newArgs;
};
class Debounce {
constructor(func, delay) {
this.func = func;
this.delay = delay;
this.timeout = null;
}
call(...args) {
if (this.timeout) {
clearTimeout(this.timeout);
}
this.timeout = setTimeout(() => {
this.func(...args);
}, this.delay);
}
}
module.exports.Debounce = Debounce;
module.exports.getSpawnArgs = getSpawnArgs;
module.exports.getLoaderArgs = getLoaderArgs;