feat(cli): add npmextra-based compile target configuration for deno builds

This commit is contained in:
2026-03-15 15:28:17 +00:00
parent 0c86e0b43e
commit 3f483ccd9c
9 changed files with 153 additions and 58 deletions

View File

@@ -1,5 +1,12 @@
# Changelog
## 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

@@ -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

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@git.zone/tsdeno',
version: '1.0.1',
version: '1.1.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;
}