6 Commits
v1.0.1 ... main

10 changed files with 219 additions and 61 deletions

View File

@@ -1,5 +1,23 @@
# Changelog
## 2026-03-15 - 1.2.0 - feat(readme)
document config-based compile targets via npmextra.json
- Rename CLI usage to passthrough mode and add config mode documentation
- Describe the npmextra.json schema for compileTargets with example target definitions
- Document the compileFromConfig() programmatic API and batch hide/restore behavior for package.json
## 2026-03-15 - 1.1.1 - fix(repository)
no changes to commit
## 2026-03-15 - 1.1.0 - feat(cli)
add npmextra-based compile target configuration for deno builds
- adds support for reading compileTargets from npmextra.json when tsdeno compile is run without explicit arguments
- exports new configuration interfaces for typed compile target definitions
- replaces direct filesystem utilities with smartfs for package.json isolation during compilation
## 2026-03-15 - 1.0.1 - fix(repo)
no changes to commit

View File

@@ -1,6 +1,6 @@
{
"name": "@git.zone/tsdeno",
"version": "1.0.1",
"version": "1.2.0",
"private": false,
"description": "A helper tool for deno compile that strips package.json to prevent devDependency bloat in compiled binaries.",
"main": "dist_ts/index.js",
@@ -25,17 +25,15 @@
],
"dependencies": {
"@push.rocks/early": "^4.0.4",
"@push.rocks/npmextra": "^5.3.3",
"@push.rocks/smartcli": "^4.0.20",
"@push.rocks/smartfile": "^11.2.0",
"@push.rocks/smartlog": "^3.2.1",
"@push.rocks/smartlog-destination-local": "^9.0.2",
"@push.rocks/smartpath": "^6.0.0",
"@push.rocks/smartshell": "^3.3.0"
"@push.rocks/smartfs": "^1.5.0",
"@push.rocks/smartshell": "^3.3.7"
},
"devDependencies": {
"@git.zone/tsbuild": "^4.1.2",
"@git.zone/tsbuild": "^4.3.0",
"@git.zone/tsrun": "^2.0.1",
"@git.zone/tstest": "^3.1.8",
"@types/node": "^22.0.0"
"@git.zone/tstest": "^3.3.2",
"@types/node": "^25.5.0"
}
}

58
pnpm-lock.yaml generated
View File

@@ -11,37 +11,31 @@ importers:
'@push.rocks/early':
specifier: ^4.0.4
version: 4.0.4
'@push.rocks/npmextra':
specifier: ^5.3.3
version: 5.3.3
'@push.rocks/smartcli':
specifier: ^4.0.20
version: 4.0.20
'@push.rocks/smartfile':
specifier: ^11.2.0
version: 11.2.7
'@push.rocks/smartlog':
specifier: ^3.2.1
version: 3.2.1
'@push.rocks/smartlog-destination-local':
specifier: ^9.0.2
version: 9.0.2
'@push.rocks/smartpath':
specifier: ^6.0.0
version: 6.0.0
'@push.rocks/smartfs':
specifier: ^1.5.0
version: 1.5.0
'@push.rocks/smartshell':
specifier: ^3.3.0
specifier: ^3.3.7
version: 3.3.7
devDependencies:
'@git.zone/tsbuild':
specifier: ^4.1.2
specifier: ^4.3.0
version: 4.3.0
'@git.zone/tsrun':
specifier: ^2.0.1
version: 2.0.1
'@git.zone/tstest':
specifier: ^3.1.8
specifier: ^3.3.2
version: 3.3.2(socks@2.8.7)(typescript@5.9.3)
'@types/node':
specifier: ^22.0.0
version: 22.19.15
specifier: ^25.5.0
version: 25.5.0
packages:
@@ -1559,6 +1553,9 @@ packages:
'@types/node@22.19.15':
resolution: {integrity: sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==}
'@types/node@25.5.0':
resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==}
'@types/ping@0.4.4':
resolution: {integrity: sha512-ifvo6w2f5eJYlXm+HiVx67iJe8WZp87sfa683nlqED5Vnt9Z93onkokNoWqOG21EaE8fMxyKPobE+mkPEyxsdw==}
@@ -3184,6 +3181,9 @@ packages:
undici-types@6.21.0:
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
undici-types@7.18.2:
resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
unified@11.0.5:
resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
@@ -5823,7 +5823,7 @@ snapshots:
'@types/clean-css@4.2.11':
dependencies:
'@types/node': 22.19.15
'@types/node': 25.5.0
source-map: 0.6.1
'@types/debug@4.1.12':
@@ -5833,7 +5833,7 @@ snapshots:
'@types/fs-extra@11.0.4':
dependencies:
'@types/jsonfile': 6.1.4
'@types/node': 22.19.15
'@types/node': 25.5.0
'@types/hast@3.0.4':
dependencies:
@@ -5853,7 +5853,7 @@ snapshots:
'@types/jsonfile@6.1.4':
dependencies:
'@types/node': 22.19.15
'@types/node': 25.5.0
'@types/mdast@4.0.4':
dependencies:
@@ -5867,11 +5867,11 @@ snapshots:
'@types/mute-stream@0.0.4':
dependencies:
'@types/node': 22.19.15
'@types/node': 25.5.0
'@types/node-forge@1.3.14':
dependencies:
'@types/node': 22.19.15
'@types/node': 25.5.0
'@types/node@16.9.1': {}
@@ -5879,6 +5879,10 @@ snapshots:
dependencies:
undici-types: 6.21.0
'@types/node@25.5.0':
dependencies:
undici-types: 7.18.2
'@types/ping@0.4.4': {}
'@types/relateurl@0.2.33': {}
@@ -5889,11 +5893,11 @@ snapshots:
'@types/tar-stream@3.1.4':
dependencies:
'@types/node': 22.19.15
'@types/node': 25.5.0
'@types/through2@2.0.41':
dependencies:
'@types/node': 22.19.15
'@types/node': 25.5.0
'@types/trusted-types@2.0.7': {}
@@ -5919,11 +5923,11 @@ snapshots:
'@types/ws@8.18.1':
dependencies:
'@types/node': 22.19.15
'@types/node': 25.5.0
'@types/yauzl@2.10.3':
dependencies:
'@types/node': 22.19.15
'@types/node': 25.5.0
optional: true
'@ungap/structured-clone@1.3.0': {}
@@ -7835,6 +7839,8 @@ snapshots:
undici-types@6.21.0: {}
undici-types@7.18.2: {}
unified@11.0.5:
dependencies:
'@types/unist': 3.0.3

View File

@@ -45,7 +45,7 @@ With `package.json` hidden, Deno only resolves dependencies declared in `deno.js
## Usage
### CLI
### CLI — Passthrough Mode
Drop-in replacement — just swap `deno compile` for `tsdeno compile`:
@@ -71,6 +71,54 @@ tsdeno compile --allow-all --no-check \
mod.ts
```
### CLI — Config Mode (npmextra.json)
For projects with multiple compile targets, you can define them in `npmextra.json` instead of writing long CLI commands. Just run `tsdeno compile` with no arguments:
```bash
tsdeno compile
```
tsdeno reads compile targets from the `@git.zone/tsdeno` key in your `npmextra.json`:
```json
{
"@git.zone/tsdeno": {
"compileTargets": [
{
"name": "myapp-linux-x64",
"entryPoint": "mod.ts",
"outDir": "./dist",
"target": "x86_64-unknown-linux-gnu",
"permissions": ["--allow-all"],
"noCheck": true
},
{
"name": "myapp-macos-arm64",
"entryPoint": "mod.ts",
"outDir": "./dist",
"target": "aarch64-apple-darwin",
"permissions": ["--allow-all"],
"noCheck": true
}
]
}
}
```
Each compile target supports these fields:
| Field | Type | Required | Description |
| -------------- | ---------- | -------- | ----------------------------------------------------- |
| `name` | `string` | ✅ | Output binary name (combined with `outDir` for path) |
| `entryPoint` | `string` | ✅ | Path to the entry TypeScript file |
| `outDir` | `string` | ✅ | Directory for the compiled output |
| `target` | `string` | ✅ | Deno compile target triple (e.g. `x86_64-unknown-linux-gnu`) |
| `permissions` | `string[]` | ❌ | Deno permission flags (e.g. `["--allow-all"]`) |
| `noCheck` | `boolean` | ❌ | Skip type checking (`--no-check`) |
In config mode, `package.json` is hidden **once** for the entire batch — all targets compile in sequence with a single hide/restore cycle.
### Programmatic API
You can also use `tsdeno` as a library in your build scripts:
@@ -81,6 +129,7 @@ import { TsDeno } from '@git.zone/tsdeno';
const tsDeno = new TsDeno(); // uses process.cwd()
// or: new TsDeno('/path/to/project')
// Passthrough mode — pass args directly
await tsDeno.compile([
'--allow-all',
'--no-check',
@@ -88,6 +137,9 @@ await tsDeno.compile([
'--target', 'x86_64-unknown-linux-gnu',
'mod.ts',
]);
// Config mode — reads compile targets from npmextra.json
await tsDeno.compileFromConfig();
```
The `TsDeno` class handles the full package.json isolation lifecycle automatically.
@@ -151,7 +203,7 @@ Use of these trademarks must comply with Task Venture Capital GmbH's Trademark G
### Company Information
Task Venture Capital GmbH
Task Venture Capital GmbH
Registered at District Court Bremen HRB 35230 HB, Germany
For any legal inquiries or further information, please contact us via email at hello@task.vc.

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@git.zone/tsdeno',
version: '1.0.1',
version: '1.2.0',
description: 'A helper tool for deno compile that strips package.json to prevent devDependency bloat in compiled binaries.'
}

View File

@@ -2,6 +2,7 @@ import * as early from '@push.rocks/early';
early.start('tsdeno');
export * from './tsdeno.classes.tsdeno.js';
export * from './tsdeno.interfaces.js';
export { runCli } from './tsdeno.cli.js';
early.stop();

View File

@@ -1,21 +1,17 @@
// node native
import * as path from 'path';
import * as fs from 'fs/promises';
export { path, fs };
export { path };
// @push.rocks scope
import * as npmextra from '@push.rocks/npmextra';
import * as smartcli from '@push.rocks/smartcli';
import * as smartfile from '@push.rocks/smartfile';
import * as smartlog from '@push.rocks/smartlog';
import * as smartlogDestinationLocal from '@push.rocks/smartlog-destination-local';
import * as smartpath from '@push.rocks/smartpath';
import { SmartFs, SmartFsProviderNode } from '@push.rocks/smartfs';
import * as smartshell from '@push.rocks/smartshell';
export {
npmextra,
smartcli,
smartfile,
smartlog,
smartlogDestinationLocal,
smartpath,
SmartFs,
SmartFsProviderNode,
smartshell,
};

View File

@@ -1,9 +1,12 @@
import * as plugins from './plugins.js';
import type { ITsdenoConfig, ICompileTarget } from './tsdeno.interfaces.js';
const shell = new plugins.smartshell.Smartshell({
executor: 'bash',
});
const smartfs = new plugins.SmartFs(new plugins.SmartFsProviderNode());
export class TsDeno {
public cwd: string;
@@ -20,13 +23,7 @@ export class TsDeno {
const packageJsonPath = plugins.path.join(this.cwd, 'package.json');
const backupPath = plugins.path.join(this.cwd, 'package.json.bak');
let hasPackageJson = false;
try {
await plugins.fs.access(packageJsonPath);
hasPackageJson = true;
} catch {
hasPackageJson = false;
}
const hasPackageJson = await smartfs.file(packageJsonPath).exists();
// Ensure --node-modules-dir=none is present
if (!args.some((arg) => arg.startsWith('--node-modules-dir'))) {
@@ -36,7 +33,7 @@ export class TsDeno {
// Temporarily hide package.json from Deno
if (hasPackageJson) {
console.log('tsdeno: temporarily hiding package.json to exclude devDependencies from bundle');
await plugins.fs.rename(packageJsonPath, backupPath);
await smartfs.file(packageJsonPath).move(backupPath);
}
try {
@@ -51,9 +48,81 @@ export class TsDeno {
} finally {
// Always restore package.json
if (hasPackageJson) {
await plugins.fs.rename(backupPath, packageJsonPath);
await smartfs.file(backupPath).move(packageJsonPath);
console.log('tsdeno: restored package.json');
}
}
}
/**
* Reads compile targets from npmextra.json and compiles each one.
* The package.json hide/restore wraps the entire loop.
*/
public async compileFromConfig(): Promise<void> {
const npmextraInstance = new plugins.npmextra.Npmextra(this.cwd);
const config = npmextraInstance.dataFor<ITsdenoConfig>('@git.zone/tsdeno', {
compileTargets: [],
});
if (config.compileTargets.length === 0) {
console.error('tsdeno: no compileTargets found in npmextra.json under "@git.zone/tsdeno"');
console.error('tsdeno: either pass args directly or add config to npmextra.json');
process.exit(1);
}
console.log(`tsdeno: found ${config.compileTargets.length} compile target(s) in npmextra.json`);
const packageJsonPath = plugins.path.join(this.cwd, 'package.json');
const backupPath = plugins.path.join(this.cwd, 'package.json.bak');
const hasPackageJson = await smartfs.file(packageJsonPath).exists();
// Hide package.json once for all targets
if (hasPackageJson) {
console.log('tsdeno: temporarily hiding package.json to exclude devDependencies from bundle');
await smartfs.file(packageJsonPath).move(backupPath);
}
try {
for (const target of config.compileTargets) {
console.log(`\ntsdeno: compiling target "${target.name}"...`);
const args = this.buildArgsFromTarget(target);
const shellCommand = `cd ${this.cwd} && deno compile ${args.join(' ')}`;
console.log(`tsdeno: running deno compile ${args.join(' ')}`);
const result = await shell.execPassthrough(shellCommand);
if (result.exitCode !== 0) {
console.error(`tsdeno: deno compile exited with code ${result.exitCode} for target "${target.name}"`);
process.exit(result.exitCode);
}
}
} finally {
if (hasPackageJson) {
await smartfs.file(backupPath).move(packageJsonPath);
console.log('\ntsdeno: restored package.json');
}
}
console.log(`\ntsdeno: all ${config.compileTargets.length} target(s) compiled successfully`);
}
private buildArgsFromTarget(target: ICompileTarget): string[] {
const args: string[] = ['--node-modules-dir=none'];
if (target.noCheck) {
args.push('--no-check');
}
if (target.permissions) {
args.push(...target.permissions);
}
const outputPath = plugins.path.join(target.outDir, target.name);
args.push('--output', outputPath);
args.push('--target', target.target);
args.push(target.entryPoint);
return args;
}
}

View File

@@ -10,8 +10,10 @@ tsdenoCli.standardCommand().subscribe(async (argvArg) => {
console.log(`@git.zone/tsdeno v${commitinfo.version}`);
console.log('');
console.log('Usage:');
console.log(' tsdeno compile [deno compile args...] Compile with package.json isolation');
console.log(' tsdeno compile Compile all targets from npmextra.json');
console.log(' tsdeno compile [deno compile args...] Compile with explicit args (passthrough)');
console.log('');
console.log('When no args are given, tsdeno reads compileTargets from npmextra.json.');
console.log('The compile command temporarily removes package.json before running');
console.log('deno compile, preventing devDependencies from bloating the binary.');
console.log('--node-modules-dir=none is added automatically.');
@@ -19,11 +21,15 @@ tsdenoCli.standardCommand().subscribe(async (argvArg) => {
tsdenoCli.addCommand('compile').subscribe(async (argvArg) => {
const tsDeno = new TsDeno();
// Pass through all args after "compile" to deno compile
const rawArgs = process.argv.slice(3);
await tsDeno.compile(rawArgs);
if (rawArgs.length === 0) {
// No args — read targets from npmextra.json
await tsDeno.compileFromConfig();
} else {
// Args provided — passthrough to deno compile
await tsDeno.compile(rawArgs);
}
});
export { tsdenoCli };

12
ts/tsdeno.interfaces.ts Normal file
View File

@@ -0,0 +1,12 @@
export interface ITsdenoConfig {
compileTargets: ICompileTarget[];
}
export interface ICompileTarget {
name: string;
entryPoint: string;
outDir: string;
target: string;
permissions?: string[];
noCheck?: boolean;
}