Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d1ef48560d | |||
| 49d62e20a4 | |||
| 4f8443d33f | |||
| 2fb838d1bd | |||
| b4a1ff5eab | |||
| d3e3905e7f | |||
| fe60a21c59 | |||
| 920095d008 | |||
| e1198c0a78 | |||
| 01b2cfe69c | |||
| b38a8236da | |||
| f136fe2e40 | |||
| 5fa93923d2 | |||
| 856099b5ed | |||
| 7aec1548eb | |||
| c269039050 | |||
| 75cb613d5e | |||
| cf05229b86 | |||
| 10b8d47a55 | |||
| 52e1a25948 | |||
| 3b8ebd41f4 | |||
| 9e839b3047 | |||
| 7ce22b0c4d | |||
| b2ce02a4fb | |||
| ca27db1d75 | |||
| c8a1582ec4 | |||
| 1e40bd5882 | |||
| 4ccb7ea9d6 | |||
| 0040325914 | |||
| f2980dc00f |
99
changelog.md
99
changelog.md
@@ -1,5 +1,104 @@
|
||||
# Changelog
|
||||
|
||||
## 2026-03-05 - 4.1.14 - fix(fs)
|
||||
replace execSync and fsync workarounds with atomic async FsHelpers operations to avoid XFS races and shell dependencies
|
||||
|
||||
- Removed child_process.execSync usage and shell mv/rm commands in mod_unpack and mod_compiler.
|
||||
- Removed syncDirectoryTree and fsync-based workaround from the compiler module.
|
||||
- Use FsHelpers.move and FsHelpers.removeDirectory (async rename/remove) for atomic filesystem operations during unpack.
|
||||
- Await performUnpack directly and simplify unpack flow to improve portability and reliability on XFS and other filesystems.
|
||||
|
||||
## 2026-03-05 - 4.1.13 - fix(mod_unpack)
|
||||
Use child_process.execSync (mv/rm) to perform unpack atomically, replacing async fs operations and logs to avoid ENOENT/partial directory listings on XFS
|
||||
|
||||
- Replaced async fs.promises.rename/rm and readdir/stat debugging with execSync rm -rf and mv operations for sequential, atomic moves
|
||||
- Imported execSync from child_process and removed verbose console logging and extra fs checks
|
||||
- Addresses race conditions observed on filesystems like XFS where libuv async operations can return partial results or ENOENT errors
|
||||
|
||||
## 2026-03-05 - 4.1.12 - fix(mod_compiler)
|
||||
replace runtime require calls with top-level imports and use execSync/path.join for filesystem sync and traversal
|
||||
|
||||
- Added top-level imports: path and execSync from child_process
|
||||
- Replaced require('child_process').execSync('sync') with execSync('sync') to force fs sync
|
||||
- Replaced require('path').join(...) with path.join(...) when recursing directories
|
||||
- Refactor is purely local/maintenance-focused (consistency and slight performance/readability improvement); no functional change expected
|
||||
|
||||
## 2026-03-05 - 4.1.11 - fix(mod_compiler)
|
||||
flush directory entries before unpack to avoid XFS delayed-log causing partial readdir results
|
||||
|
||||
- Add fs import and call child_process.execSync('sync') before unpacking compiled output to force kernel-level sync
|
||||
- Add syncDirectoryTree(dir) to recursively open and fsync directory file descriptors and traverse subdirectories
|
||||
- Call syncDirectoryTree on the destination directory before performing unpack to ensure TypeScript writeFileSync entries are committed (prevents partial readdir results on XFS)
|
||||
- Errors during directory sync are ignored to avoid breaking normal flow
|
||||
|
||||
## 2026-03-05 - 4.1.10 - fix(unpack)
|
||||
use atomic renames to flatten nested output and make unpacking more reliable
|
||||
|
||||
- Replace per-entry moves with an atomic 3-step strategy: rename nested dir to a temp location, remove the destination dir, then rename temp back to the destination to avoid partial readdir/move under signal pressure.
|
||||
- FsHelpers.move switched from sync rename to fs.promises.rename to work with async flows.
|
||||
- Use fs.promises.rm with retries and explicit temp-dir cleanup to handle previous failed runs.
|
||||
- Add diagnostic logging and verification of intermediate states.
|
||||
- Removed the older removeSiblingDirectories and moveNestedContentsUp methods in favor of the new rename-based approach.
|
||||
|
||||
## 2026-03-05 - 4.1.9 - fix(fs)
|
||||
improve filesystem helpers: use sync rename for reliability on certain filesystems; retry rmdir with delays and avoid recursive rm; bump @push.rocks/smartfs to ^1.3.2
|
||||
|
||||
- move(): use fs.renameSync to improve reliability on XFS/mounted filesystems where async rename can be interrupted by process managers
|
||||
- removeEmptyDirectory(): retry fs.promises.rmdir up to 5 attempts with incremental delays; return on ENOENT; avoid recursive rm on ENOTEMPTY to prevent removing still-valid references
|
||||
- Dependency bump: @push.rocks/smartfs from ^1.3.1 to ^1.3.2
|
||||
|
||||
## 2026-03-05 - 4.1.8 - fix(unpack)
|
||||
catch unpack errors and add verbose unpack logging
|
||||
|
||||
- Wrap performUnpack call in the compiler to catch and log exceptions so unpack failures don't crash the process
|
||||
- Log the number of directories and files being unpacked when moving nested contents to aid debugging
|
||||
|
||||
## 2026-03-05 - 4.1.7 - fix(fs/compiler/unpack)
|
||||
robustify directory removal and remove noisy diagnostic logging
|
||||
|
||||
- Use fs.promises.rm with force, maxRetries and retryDelay in removeDirectory to reduce intermittent failures during removals.
|
||||
- Handle ENOTEMPTY in removeEmptyDirectory by falling back to a recursive rm and ignore ENOENT to avoid errors from transient filesystem metadata lag.
|
||||
- Remove several diagnostic/verbose console logs in the compiler and unpacker paths to reduce noisy output and simplify flow.
|
||||
- Short-circuit unpack logic when no nesting is detected to avoid unnecessary work and logs.
|
||||
- No public API or behavior-breaking changes; suitable for a patch release.
|
||||
|
||||
## 2026-03-05 - 4.1.6 - fix(mod_compiler)
|
||||
add diagnostic logging to report dist_ts and output directory contents after each compilation task and after import-path rewriting
|
||||
|
||||
- Adds DIAG-CROSSTASK logs to inspect dist_ts subdirectories after each task when not in quiet or JSON mode
|
||||
- Adds DIAG-FINAL logs to report directory names and file counts for each successful output dir after import-path rewriting
|
||||
- Diagnostics use dynamic fs import and are non-intrusive: gated by isQuiet/isJson and errors are ignored
|
||||
|
||||
## 2026-03-05 - 4.1.5 - fix(diagnostics)
|
||||
add diagnostic logging around compilation and unpack to aid troubleshooting
|
||||
|
||||
- Enable TypeScript CompilerOptions.listEmittedFiles to surface emitted file info
|
||||
- Log destination directory contents and emitted file/error counts after compile and after unpack (only when not quiet and not json)
|
||||
- Add unpack-specific diagnostic logs: shouldUnpack skip, detectNesting result and nestedPath, and nested/destination directory listings before removing sibling directories
|
||||
- All logs use console.log and are wrapped in try/catch to avoid throwing; changes are informational and non-breaking
|
||||
|
||||
## 2026-03-05 - 4.1.4 - fix(deps)
|
||||
bump @git.zone/tspublish dependency to ^1.11.2
|
||||
|
||||
- Updated @git.zone/tspublish from ^1.11.0 to ^1.11.2 in package.json
|
||||
|
||||
## 2026-03-04 - 4.1.3 - fix(deps)
|
||||
bump dependencies: @push.rocks/smartcli, @push.rocks/smartlog, @git.zone/tstest, and @types/node to their newer versions
|
||||
|
||||
- @push.rocks/smartcli: ^4.0.19 -> ^4.0.20
|
||||
- @push.rocks/smartlog: ^3.1.10 -> ^3.2.1
|
||||
- @git.zone/tstest: ^3.1.4 -> ^3.2.0
|
||||
- @types/node: ^25.0.3 -> ^25.3.3
|
||||
- Non-breaking dependency version bumps
|
||||
|
||||
## 2026-01-04 - 4.1.0 - feat(docs)
|
||||
update README with improved docs and monorepo/tspublish guidance; namespace and extend npmextra.json with release registries; bump several dependencies
|
||||
|
||||
- Bumped dependencies: @git.zone/tspublish ^1.10.3 → ^1.11.0, @push.rocks/smartfs ^1.2.0 → ^1.3.1, @git.zone/tstest ^3.1.3 → ^3.1.4, @types/node ^25.0.1 → ^25.0.3
|
||||
- npmextra.json reorganized: replaced legacy keys with namespaced entries (@git.zone/cli, @git.zone/tsdoc) and added @ship.zone/szci; added release configuration with registries [https://verdaccio.lossless.digital, https://registry.npmjs.org] and public accessLevel
|
||||
- README rewritten: improved formatting, added emojis and tables, new 'Issue Reporting and Security' section, monorepo/tspublish usage docs, auto-unpack and decorator guidance, CI/CD and troubleshooting improvements
|
||||
- Non-breaking changes; recommended next semantic version bump is minor
|
||||
|
||||
## 2025-12-14 - 4.0.2 - fix(TsCompiler)
|
||||
Clear output directories before compilation to ensure clean builds and avoid stale files
|
||||
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
{
|
||||
"npmci": {
|
||||
"npmGlobalTools": [],
|
||||
"npmAccessLevel": "public"
|
||||
},
|
||||
"gitzone": {
|
||||
"@git.zone/cli": {
|
||||
"projectType": "npm",
|
||||
"module": {
|
||||
"githost": "gitlab.com",
|
||||
@@ -23,9 +19,19 @@
|
||||
"development",
|
||||
"API"
|
||||
]
|
||||
},
|
||||
"release": {
|
||||
"registries": [
|
||||
"https://verdaccio.lossless.digital",
|
||||
"https://registry.npmjs.org"
|
||||
],
|
||||
"accessLevel": "public"
|
||||
}
|
||||
},
|
||||
"tsdoc": {
|
||||
"@git.zone/tsdoc": {
|
||||
"legal": "\n## License and Legal Information\n\nThis repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. \n\n**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.\n\n### Trademarks\n\nThis project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.\n\n### Company Information\n\nTask Venture Capital GmbH \nRegistered at District court Bremen HRB 35230 HB, Germany\n\nFor any legal inquiries or if you require further information, please contact us via email at hello@task.vc.\n\nBy using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.\n"
|
||||
},
|
||||
"@ship.zone/szci": {
|
||||
"npmGlobalTools": []
|
||||
}
|
||||
}
|
||||
14
package.json
14
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@git.zone/tsbuild",
|
||||
"version": "4.0.2",
|
||||
"version": "4.1.14",
|
||||
"private": false,
|
||||
"description": "A tool for compiling TypeScript files using the latest nightly features, offering flexible APIs and a CLI for streamlined development.",
|
||||
"main": "dist_ts/index.js",
|
||||
@@ -36,21 +36,21 @@
|
||||
},
|
||||
"homepage": "https://code.foss.global/git.zone/tsbuild#README",
|
||||
"dependencies": {
|
||||
"@git.zone/tspublish": "^1.10.3",
|
||||
"@git.zone/tspublish": "^1.11.2",
|
||||
"@push.rocks/early": "^4.0.4",
|
||||
"@push.rocks/smartcli": "^4.0.19",
|
||||
"@push.rocks/smartcli": "^4.0.20",
|
||||
"@push.rocks/smartdelay": "^3.0.5",
|
||||
"@push.rocks/smartfile": "^13.1.2",
|
||||
"@push.rocks/smartfs": "^1.2.0",
|
||||
"@push.rocks/smartlog": "^3.1.10",
|
||||
"@push.rocks/smartfs": "^1.3.2",
|
||||
"@push.rocks/smartlog": "^3.2.1",
|
||||
"@push.rocks/smartpath": "^6.0.0",
|
||||
"@push.rocks/smartpromise": "^4.2.3",
|
||||
"typescript": "5.9.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@git.zone/tsrun": "^2.0.1",
|
||||
"@git.zone/tstest": "^3.1.3",
|
||||
"@types/node": "^25.0.1"
|
||||
"@git.zone/tstest": "^3.2.0",
|
||||
"@types/node": "^25.3.3"
|
||||
},
|
||||
"files": [
|
||||
"ts/**/*",
|
||||
|
||||
4284
pnpm-lock.yaml
generated
4284
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
238
readme.md
238
readme.md
@@ -1,8 +1,12 @@
|
||||
# @git.zone/tsbuild
|
||||
|
||||
A powerful, modern TypeScript build tool with smart defaults, full tsconfig.json support, and automatic output directory management.
|
||||
A powerful, modern TypeScript build tool with smart defaults, full tsconfig.json support, and automatic output directory management. 🚀
|
||||
|
||||
## Install
|
||||
## Issue Reporting and Security
|
||||
|
||||
For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
|
||||
|
||||
## 📦 Install
|
||||
|
||||
```bash
|
||||
# Using pnpm (recommended)
|
||||
@@ -12,20 +16,22 @@ pnpm install @git.zone/tsbuild --save-dev
|
||||
npm install @git.zone/tsbuild --save-dev
|
||||
```
|
||||
|
||||
## Why tsbuild?
|
||||
## ⚡ Why tsbuild?
|
||||
|
||||
- **Smart tsconfig.json Integration** - Respects all your compiler options with intelligent merging
|
||||
- **Protected Defaults** - Critical build settings are safeguarded while staying flexible
|
||||
- **Zero Config** - Works perfectly without tsconfig.json
|
||||
- **Glob Pattern Support** - Compile multiple directories with a single command
|
||||
- **Dependency-Aware** - Automatically orders compilation based on module dependencies
|
||||
- **Type Checking** - Validate code without emitting files
|
||||
- **Clean Builds** - Automatically clears output directories before compilation
|
||||
- **Auto-Unpack** - Flattens nested output directories automatically
|
||||
- **CI/CD Ready** - JSON output mode and proper exit codes
|
||||
- **Modern Defaults** - ESNext, NodeNext modules, decorators out of the box
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| 🔧 **Smart tsconfig.json Integration** | Respects all your compiler options with intelligent merging |
|
||||
| 🛡️ **Protected Defaults** | Critical build settings are safeguarded while staying flexible |
|
||||
| 🎯 **Zero Config** | Works perfectly without tsconfig.json |
|
||||
| 🌐 **Glob Pattern Support** | Compile multiple directories with a single command |
|
||||
| 📊 **Dependency-Aware** | Automatically orders compilation based on module dependencies |
|
||||
| ✅ **Type Checking** | Validate code without emitting files |
|
||||
| 🧹 **Clean Builds** | Automatically clears output directories before compilation |
|
||||
| 📁 **Auto-Unpack** | Flattens nested output directories automatically |
|
||||
| 🔄 **CI/CD Ready** | JSON output mode and proper exit codes |
|
||||
| ✨ **Modern Defaults** | ESNext, NodeNext modules, decorators out of the box |
|
||||
|
||||
## Quick Start
|
||||
## 🚀 Quick Start
|
||||
|
||||
### CLI Usage
|
||||
|
||||
@@ -40,8 +46,8 @@ Compiles `./ts/**/*.ts` to `./dist_ts/`
|
||||
npx tsbuild custom src utils
|
||||
```
|
||||
Compiles:
|
||||
- `./src/**/*.ts` to `./dist_src/`
|
||||
- `./utils/**/*.ts` to `./dist_utils/`
|
||||
- `./src/**/*.ts` → `./dist_src/`
|
||||
- `./utils/**/*.ts` → `./dist_utils/`
|
||||
|
||||
**Auto-discover and compile in dependency order:**
|
||||
```bash
|
||||
@@ -91,7 +97,7 @@ await compiler.compileGlob({
|
||||
});
|
||||
```
|
||||
|
||||
## CLI Commands
|
||||
## 🛠️ CLI Commands
|
||||
|
||||
### 1. Default Build
|
||||
|
||||
@@ -102,12 +108,14 @@ npx tsbuild [options]
|
||||
Compiles all TypeScript files from `./ts/` to `./dist_ts/`
|
||||
|
||||
**Options:**
|
||||
- `--skiplibcheck` - Skip type checking of declaration files
|
||||
- `--confirmskiplibcheck` - Skip lib check with extended warning (5s pause)
|
||||
- `--disallowimplicitany` - Disallow implicit `any` types
|
||||
- `--commonjs` - Use CommonJS instead of ESNext modules
|
||||
- `--json` - Output results as JSON (for CI/CD)
|
||||
- `--quiet` - Suppress console output
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--skiplibcheck` | Skip type checking of declaration files |
|
||||
| `--confirmskiplibcheck` | Skip lib check with extended warning (5s pause) |
|
||||
| `--disallowimplicitany` | Disallow implicit `any` types |
|
||||
| `--commonjs` | Use CommonJS instead of ESNext modules |
|
||||
| `--json` | Output results as JSON (for CI/CD) |
|
||||
| `--quiet` | Suppress console output |
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
@@ -141,7 +149,7 @@ npx tsbuild custom src utils
|
||||
npx tsbuild custom api models services --commonjs
|
||||
```
|
||||
|
||||
### 3. TSFolders (Dependency-Aware)
|
||||
### 3. TSFolders (Dependency-Aware) 📊
|
||||
|
||||
```bash
|
||||
npx tsbuild tsfolders [options]
|
||||
@@ -164,7 +172,7 @@ TypeScript Folder Compilation Plan (5 folders)
|
||||
5/5 ts_modules
|
||||
```
|
||||
|
||||
### 4. Emit Check
|
||||
### 4. Emit Check ✓
|
||||
|
||||
```bash
|
||||
npx tsbuild emitcheck <file_or_pattern> [more...] [options]
|
||||
@@ -184,7 +192,7 @@ npx tsbuild emitcheck "src/**/*.ts" "test/**/*.ts"
|
||||
- `0` - All files can be emitted
|
||||
- `1` - One or more files have errors
|
||||
|
||||
### 5. Type Check
|
||||
### 5. Type Check 🔍
|
||||
|
||||
```bash
|
||||
npx tsbuild check [pattern] [more...] [options]
|
||||
@@ -210,7 +218,7 @@ npx tsbuild check
|
||||
# All default type checks passed!
|
||||
```
|
||||
|
||||
## API Reference
|
||||
## 📖 API Reference
|
||||
|
||||
### TsCompiler Class
|
||||
|
||||
@@ -390,7 +398,7 @@ const cli = new TsBuildCli('/path/to/project');
|
||||
cli.run();
|
||||
```
|
||||
|
||||
## Configuration
|
||||
## ⚙️ Configuration
|
||||
|
||||
### tsconfig.json Support
|
||||
|
||||
@@ -411,24 +419,28 @@ tsbuild fully supports all compiler options from `tsconfig.json`. Your project c
|
||||
}
|
||||
```
|
||||
|
||||
### Configuration Priority (5 Levels)
|
||||
### Configuration Priority (5 Levels) 📋
|
||||
|
||||
When multiple configuration sources exist, they merge in this order (later overrides earlier):
|
||||
|
||||
1. **Default Options** - tsbuild's sensible defaults
|
||||
2. **tsconfig.json** - All options from your tsconfig.json (if present)
|
||||
3. **Protected Defaults** - Critical options for build integrity
|
||||
4. **Programmatic Options** - Options passed to API functions
|
||||
5. **CLI Flags** - Command-line arguments (highest priority)
|
||||
| Priority | Source | Description |
|
||||
|----------|--------|-------------|
|
||||
| 1️⃣ | **Default Options** | tsbuild's sensible defaults |
|
||||
| 2️⃣ | **tsconfig.json** | All options from your tsconfig.json (if present) |
|
||||
| 3️⃣ | **Protected Defaults** | Critical options for build integrity |
|
||||
| 4️⃣ | **Programmatic Options** | Options passed to API functions |
|
||||
| 5️⃣ | **CLI Flags** | Command-line arguments (highest priority) |
|
||||
|
||||
### Protected Options
|
||||
### Protected Options 🛡️
|
||||
|
||||
These options cannot be overridden by tsconfig.json alone (but can be overridden programmatically or via CLI):
|
||||
|
||||
- **`outDir: 'dist_ts/'`** - Required for automatic path transformations
|
||||
- **`noEmitOnError: true`** - Prevents broken builds from being emitted
|
||||
- **`declaration: true`** - Ensures `.d.ts` files for library consumers
|
||||
- **`inlineSourceMap: true`** - Consistent debugging experience
|
||||
| Option | Value | Reason |
|
||||
|--------|-------|--------|
|
||||
| `outDir` | `'dist_ts/'` | Required for automatic path transformations |
|
||||
| `noEmitOnError` | `true` | Prevents broken builds from being emitted |
|
||||
| `declaration` | `true` | Ensures `.d.ts` files for library consumers |
|
||||
| `inlineSourceMap` | `true` | Consistent debugging experience |
|
||||
|
||||
### Default Compiler Options
|
||||
|
||||
@@ -451,7 +463,7 @@ When no tsconfig.json exists:
|
||||
}
|
||||
```
|
||||
|
||||
### Path Transformation
|
||||
### Path Transformation 🔄
|
||||
|
||||
tsbuild automatically transforms path mappings:
|
||||
|
||||
@@ -468,12 +480,12 @@ tsbuild automatically transforms path mappings:
|
||||
|
||||
**Automatic transformation:**
|
||||
```
|
||||
./ts_models/* -> ./dist_ts_models/*
|
||||
./ts_models/* → ./dist_ts_models/*
|
||||
```
|
||||
|
||||
## Features
|
||||
## ✨ Features
|
||||
|
||||
### Clean Builds
|
||||
### Clean Builds 🧹
|
||||
|
||||
Output directories are automatically cleared before compilation:
|
||||
|
||||
@@ -484,35 +496,129 @@ Compiling 14 files from ./ts/**/*.ts
|
||||
|
||||
This ensures no stale files remain from previous builds.
|
||||
|
||||
### Auto-Unpack
|
||||
### Monorepo Support with tspublish 📦
|
||||
|
||||
tsbuild is designed to work seamlessly with [@git.zone/tspublish](https://code.foss.global/git.zone/tspublish) for monorepo workflows. This enables building and publishing multiple packages from a single repository.
|
||||
|
||||
#### Directory Structure
|
||||
|
||||
```
|
||||
my-project/
|
||||
├── ts/ # Main package source
|
||||
├── ts_interfaces/ # Shared interfaces (order: 1)
|
||||
├── ts_shared/ # Shared utilities (order: 2)
|
||||
├── ts_core/ # Core logic (order: 3)
|
||||
├── ts_web/ # Web-specific code (order: 4)
|
||||
└── ts_node/ # Node-specific code (order: 5)
|
||||
```
|
||||
|
||||
Each `ts_*` folder can contain its own `tspublish.json` to configure compilation and publishing behavior.
|
||||
|
||||
#### tspublish.json Configuration
|
||||
|
||||
Create a `tspublish.json` in each `ts_*` folder:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "@myorg/core",
|
||||
"order": 3,
|
||||
"unpack": true,
|
||||
"dependencies": ["ts_interfaces", "ts_shared"]
|
||||
}
|
||||
```
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `name` | string | — | Package name for publishing |
|
||||
| `order` | number | `Infinity` | Build sequence (lower builds first) |
|
||||
| `unpack` | boolean | `true` | Flatten nested output directories |
|
||||
| `dependencies` | string[] | — | Monorepo dependencies to bundle |
|
||||
|
||||
#### Build Order
|
||||
|
||||
The `tsfolders` command respects the `order` property:
|
||||
|
||||
```bash
|
||||
npx tsbuild tsfolders
|
||||
```
|
||||
|
||||
**Default ordering (without tspublish.json):**
|
||||
1. `ts_interfaces` - always first (shared types)
|
||||
2. `ts_shared` - always second (shared utilities)
|
||||
3. Other folders sorted by `order` property
|
||||
4. Folders without `order` are built last
|
||||
|
||||
**Example output:**
|
||||
```
|
||||
TypeScript Folder Compilation Plan (5 folders)
|
||||
1/5 ts_interfaces (order: 1)
|
||||
2/5 ts_shared (order: 2)
|
||||
3/5 ts_core (order: 3)
|
||||
4/5 ts_web (order: 4)
|
||||
5/5 ts_node (order: 5)
|
||||
```
|
||||
|
||||
### Auto-Unpack 📁
|
||||
|
||||
When TypeScript compiles files that import from sibling directories, it creates nested output:
|
||||
|
||||
```
|
||||
dist_ts_core/
|
||||
ts_core/ <- nested output
|
||||
ts_shared/ <- pulled-in dependency
|
||||
ts_core/ ← nested output
|
||||
ts_shared/ ← pulled-in dependency
|
||||
```
|
||||
|
||||
tsbuild automatically flattens this to:
|
||||
|
||||
```
|
||||
dist_ts_core/
|
||||
index.js <- flat
|
||||
index.js ← flat, clean structure
|
||||
```
|
||||
|
||||
Configure via `tspublish.json` in source folder:
|
||||
This is especially important for monorepos where packages import from each other.
|
||||
|
||||
**Control via tspublish.json:**
|
||||
```json
|
||||
{
|
||||
"unpack": true
|
||||
}
|
||||
```
|
||||
|
||||
Set `"unpack": false` to disable.
|
||||
- `"unpack": true` (default) - Flatten nested directories after compilation
|
||||
- `"unpack": false` - Keep original nested structure
|
||||
|
||||
### Decorator Support
|
||||
**What gets unpacked:**
|
||||
- The source folder's contents (`ts_core/`) are moved to the root of `dist_ts_core/`
|
||||
- Sibling folders (`ts_shared/`) that were pulled in are removed (they have their own dist)
|
||||
|
||||
First-class decorator support out of the box:
|
||||
### Decorator Support 🎨
|
||||
|
||||
tsbuild supports both **TC39 standard decorators** (recommended) and legacy experimental decorators.
|
||||
|
||||
**TC39 Standard Decorators (Preferred)** 🌟
|
||||
|
||||
We strongly recommend using TC39 standard decorators for all new code. They are the official JavaScript standard and provide better semantics:
|
||||
|
||||
```typescript
|
||||
// TC39 standard decorator
|
||||
function log(target: any, context: ClassMethodDecoratorContext) {
|
||||
return function (...args: any[]) {
|
||||
console.log(`Calling ${String(context.name)}`);
|
||||
return target.apply(this, args);
|
||||
};
|
||||
}
|
||||
|
||||
class UserService {
|
||||
@log
|
||||
getUser(id: string) {
|
||||
return { id, name: 'John' };
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Legacy Decorators (Backwards Compatibility)**
|
||||
|
||||
For existing projects using frameworks like NestJS, TypeORM, or Angular that still require experimental decorators:
|
||||
|
||||
```typescript
|
||||
@Injectable()
|
||||
@@ -524,9 +630,19 @@ class UserService {
|
||||
}
|
||||
```
|
||||
|
||||
Works with NestJS, TypeORM, Inversify, Angular, and other DI frameworks.
|
||||
Enable legacy decorators in your `tsconfig.json`:
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## CI/CD Integration
|
||||
> 💡 **Tip:** When starting new projects or migrating existing ones, prefer TC39 decorators. They're the future of JavaScript and don't require experimental flags.
|
||||
|
||||
## 🔄 CI/CD Integration
|
||||
|
||||
### GitHub Actions
|
||||
|
||||
@@ -578,7 +694,7 @@ npx tsbuild --json --quiet
|
||||
}
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
## 🔧 Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
@@ -607,27 +723,23 @@ npx tsbuild --skiplibcheck
|
||||
|
||||
Only use this if you trust your dependencies' type definitions.
|
||||
|
||||
## Issue Reporting
|
||||
|
||||
For reporting issues or vulnerabilities, please visit our community at [community.foss.global](https://community.foss.global). We're looking forward to your contribution!
|
||||
|
||||
For repository access: [code.foss.global/git.zone/tsbuild](https://code.foss.global/git.zone/tsbuild)
|
||||
|
||||
## License and Legal Information
|
||||
|
||||
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
||||
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./LICENSE) file.
|
||||
|
||||
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
||||
|
||||
### Trademarks
|
||||
|
||||
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.
|
||||
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
|
||||
|
||||
Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
|
||||
|
||||
### Company Information
|
||||
|
||||
Task Venture Capital GmbH
|
||||
Registered at District Court Bremen HRB 35230 HB, Germany
|
||||
|
||||
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
|
||||
For any legal inquiries or further information, please contact us via email at hello@task.vc.
|
||||
|
||||
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@git.zone/tsbuild',
|
||||
version: '4.0.2',
|
||||
version: '4.1.14',
|
||||
description: 'A tool for compiling TypeScript files using the latest nightly features, offering flexible APIs and a CLI for streamlined development.'
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ early.start('tsbuild');
|
||||
export * from './mod_fs/index.js';
|
||||
export * from './mod_config/index.js';
|
||||
export * from './mod_unpack/index.js';
|
||||
export * from './mod_pathrewrite/index.js';
|
||||
export * from './mod_compiler/index.js';
|
||||
export * from './mod_cli/index.js';
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import * as smartpath from '@push.rocks/smartpath';
|
||||
import { TsConfig } from '../mod_config/index.js';
|
||||
import { FsHelpers } from '../mod_fs/index.js';
|
||||
import { performUnpack } from '../mod_unpack/index.js';
|
||||
import { TsPathRewriter } from '../mod_pathrewrite/index.js';
|
||||
|
||||
/**
|
||||
* Interface for error summary data
|
||||
@@ -307,6 +308,7 @@ export class TsCompiler {
|
||||
): Promise<ICompileResult> {
|
||||
const emittedFiles: string[] = [];
|
||||
const errorSummaries: IErrorSummary[] = [];
|
||||
const successfulOutputDirs: string[] = [];
|
||||
|
||||
const totalTasks = Object.keys(globPatterns).length;
|
||||
let currentTask = 0;
|
||||
@@ -347,6 +349,7 @@ export class TsCompiler {
|
||||
const options: CompilerOptions = {
|
||||
...customOptions,
|
||||
outDir: destDir,
|
||||
listEmittedFiles: true,
|
||||
};
|
||||
|
||||
currentTask++;
|
||||
@@ -365,6 +368,22 @@ export class TsCompiler {
|
||||
// Perform unpack if compilation succeeded
|
||||
if (result.errorSummary.totalErrors === 0) {
|
||||
await performUnpack(pattern, destDir, this.cwd);
|
||||
successfulOutputDirs.push(destDir);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Rewrite import paths in all output directories to handle cross-module references
|
||||
// This must happen after ALL compilations so all destination folders exist
|
||||
// Use fromProjectDirectory to detect ALL ts_* folders, not just the ones being compiled
|
||||
if (successfulOutputDirs.length > 0) {
|
||||
const rewriter = await TsPathRewriter.fromProjectDirectory(this.cwd);
|
||||
let totalRewritten = 0;
|
||||
for (const outputDir of successfulOutputDirs) {
|
||||
totalRewritten += await rewriter.rewriteDirectory(outputDir);
|
||||
}
|
||||
if (totalRewritten > 0 && !isQuiet && !isJson) {
|
||||
console.log(` 🔄 Rewrote import paths in ${totalRewritten} file${totalRewritten !== 1 ? 's' : ''}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ export class FsHelpers {
|
||||
* Remove a directory recursively
|
||||
*/
|
||||
public static async removeDirectory(dirPath: string): Promise<void> {
|
||||
await fs.promises.rm(dirPath, { recursive: true });
|
||||
await fs.promises.rm(dirPath, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,6 +139,29 @@ export class FsHelpers {
|
||||
* Remove an empty directory
|
||||
*/
|
||||
public static async removeEmptyDirectory(dirPath: string): Promise<void> {
|
||||
await fs.promises.rmdir(dirPath);
|
||||
// Retry rmdir with delays to handle filesystem metadata lag (XFS, NFS, etc.)
|
||||
// NEVER use recursive rm here — if rmdir fails with ENOTEMPTY, entries may
|
||||
// still be valid references to renamed files/dirs that haven't fully detached
|
||||
for (let attempt = 0; attempt < 5; attempt++) {
|
||||
try {
|
||||
await fs.promises.rmdir(dirPath);
|
||||
return;
|
||||
} catch (err: any) {
|
||||
if (err.code === 'ENOENT') {
|
||||
return; // Already gone
|
||||
}
|
||||
if (err.code === 'ENOTEMPTY' && attempt < 4) {
|
||||
// Wait for filesystem metadata to catch up
|
||||
await new Promise(resolve => setTimeout(resolve, 100 * (attempt + 1)));
|
||||
continue;
|
||||
}
|
||||
// Final attempt failed or non-retryable error — leave directory in place
|
||||
// It will be cleaned up by the next build's "clear output directory" step
|
||||
if (err.code === 'ENOTEMPTY') {
|
||||
return;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
233
ts/mod_pathrewrite/classes.tspathrewriter.ts
Normal file
233
ts/mod_pathrewrite/classes.tspathrewriter.ts
Normal file
@@ -0,0 +1,233 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { FsHelpers } from '../mod_fs/index.js';
|
||||
|
||||
/**
|
||||
* Interface for folder mapping between source and destination
|
||||
*/
|
||||
export interface IFolderMapping {
|
||||
sourceFolder: string; // e.g., 'ts_shared'
|
||||
destFolder: string; // e.g., 'dist_ts_shared'
|
||||
}
|
||||
|
||||
/**
|
||||
* TsPathRewriter handles rewriting import paths in compiled JavaScript files.
|
||||
*
|
||||
* When TypeScript compiles files that import from sibling directories like:
|
||||
* import { helper } from '../ts_shared/helper.js';
|
||||
*
|
||||
* This class rewrites them to point to the compiled output directories:
|
||||
* import { helper } from '../dist_ts_shared/helper.js';
|
||||
*
|
||||
* This is necessary because the unpack feature flattens nested output structures,
|
||||
* changing the relative paths between modules.
|
||||
*/
|
||||
export class TsPathRewriter {
|
||||
private mappings: IFolderMapping[];
|
||||
|
||||
constructor(mappings: IFolderMapping[]) {
|
||||
this.mappings = mappings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a TsPathRewriter from glob patterns used in compilation
|
||||
*
|
||||
* @param globPatterns - Map of source patterns to destination directories
|
||||
* e.g., { './ts_core/**\/*.ts': './dist_ts_core', './ts_shared/**\/*.ts': './dist_ts_shared' }
|
||||
* @returns TsPathRewriter instance with extracted folder mappings
|
||||
*/
|
||||
public static fromGlobPatterns(globPatterns: Record<string, string>): TsPathRewriter {
|
||||
const mappings: IFolderMapping[] = [];
|
||||
|
||||
for (const [sourcePattern, destDir] of Object.entries(globPatterns)) {
|
||||
const sourceFolder = FsHelpers.extractSourceFolder(sourcePattern);
|
||||
const destFolder = FsHelpers.extractSourceFolder(destDir);
|
||||
|
||||
if (sourceFolder && destFolder) {
|
||||
mappings.push({ sourceFolder, destFolder });
|
||||
}
|
||||
}
|
||||
|
||||
return new TsPathRewriter(mappings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a TsPathRewriter by auto-detecting all ts_* folders in the project directory.
|
||||
* This ensures that cross-module imports are rewritten even when compiling a single module.
|
||||
*
|
||||
* For example, if the project has ts/, ts_shared/, ts_web/ folders:
|
||||
* - ts → dist_ts
|
||||
* - ts_shared → dist_ts_shared
|
||||
* - ts_web → dist_ts_web
|
||||
*
|
||||
* @param cwd - The project root directory
|
||||
* @returns TsPathRewriter instance with all detected folder mappings
|
||||
*/
|
||||
public static async fromProjectDirectory(cwd: string): Promise<TsPathRewriter> {
|
||||
const mappings: IFolderMapping[] = [];
|
||||
|
||||
try {
|
||||
const entries = await FsHelpers.listDirectory(cwd);
|
||||
|
||||
for (const entry of entries) {
|
||||
// Match folders starting with 'ts' (ts, ts_shared, ts_web, ts_core, etc.)
|
||||
if (entry.isDirectory && /^ts(_|$)/.test(entry.name)) {
|
||||
const sourceFolder = entry.name;
|
||||
const destFolder = `dist_${sourceFolder}`;
|
||||
mappings.push({ sourceFolder, destFolder });
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Directory listing failed, return empty mappings
|
||||
}
|
||||
|
||||
return new TsPathRewriter(mappings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current folder mappings
|
||||
*/
|
||||
public getMappings(): IFolderMapping[] {
|
||||
return [...this.mappings];
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrite import paths in a single file
|
||||
*
|
||||
* @param filePath - Absolute path to the file to rewrite
|
||||
* @returns true if file was modified, false otherwise
|
||||
*/
|
||||
public async rewriteFile(filePath: string): Promise<boolean> {
|
||||
// Only process .js and .d.ts files
|
||||
if (!filePath.endsWith('.js') && !filePath.endsWith('.d.ts')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const content = await fs.promises.readFile(filePath, 'utf8');
|
||||
const rewritten = this.rewriteContent(content);
|
||||
|
||||
if (rewritten !== content) {
|
||||
await fs.promises.writeFile(filePath, rewritten, 'utf8');
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (error) {
|
||||
// File doesn't exist or other error
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrite import paths in all .js and .d.ts files in a directory (recursively)
|
||||
*
|
||||
* @param dirPath - Absolute path to the directory
|
||||
* @returns Number of files modified
|
||||
*/
|
||||
public async rewriteDirectory(dirPath: string): Promise<number> {
|
||||
let modifiedCount = 0;
|
||||
|
||||
const processDir = async (currentDir: string): Promise<void> => {
|
||||
const entries = await FsHelpers.listDirectory(currentDir);
|
||||
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(currentDir, entry.name);
|
||||
|
||||
if (entry.isDirectory) {
|
||||
await processDir(fullPath);
|
||||
} else if (entry.isFile && (entry.name.endsWith('.js') || entry.name.endsWith('.d.ts'))) {
|
||||
const wasModified = await this.rewriteFile(fullPath);
|
||||
if (wasModified) {
|
||||
modifiedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (await FsHelpers.directoryExists(dirPath)) {
|
||||
await processDir(dirPath);
|
||||
}
|
||||
|
||||
return modifiedCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrite import paths in content string
|
||||
*
|
||||
* This method only modifies actual import/export/require statements,
|
||||
* not arbitrary strings or comments that might contain similar patterns.
|
||||
*
|
||||
* @param content - File content to process
|
||||
* @returns Rewritten content
|
||||
*/
|
||||
public rewriteContent(content: string): string {
|
||||
if (this.mappings.length === 0) {
|
||||
return content;
|
||||
}
|
||||
|
||||
let result = content;
|
||||
|
||||
for (const { sourceFolder, destFolder } of this.mappings) {
|
||||
// Skip if source and dest are the same
|
||||
if (sourceFolder === destFolder) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Pattern 1: ES Module imports/exports with 'from'
|
||||
// Matches: from '../ts_shared/...' or from '../../ts_shared/...'
|
||||
// Also handles: export { x } from '../ts_shared/...'
|
||||
const fromPattern = new RegExp(
|
||||
`(from\\s*['"])((?:\\.\\.\\/)+)${this.escapeRegex(sourceFolder)}(\\/[^'"]*['"])`,
|
||||
'g'
|
||||
);
|
||||
result = result.replace(fromPattern, `$1$2${destFolder}$3`);
|
||||
|
||||
// Pattern 2: Dynamic imports
|
||||
// Matches: import('../ts_shared/...')
|
||||
const dynamicImportPattern = new RegExp(
|
||||
`(import\\s*\\(\\s*['"])((?:\\.\\.\\/)+)${this.escapeRegex(sourceFolder)}(\\/[^'"]*['"]\\s*\\))`,
|
||||
'g'
|
||||
);
|
||||
result = result.replace(dynamicImportPattern, `$1$2${destFolder}$3`);
|
||||
|
||||
// Pattern 3: CommonJS require
|
||||
// Matches: require('../ts_shared/...')
|
||||
const requirePattern = new RegExp(
|
||||
`(require\\s*\\(\\s*['"])((?:\\.\\.\\/)+)${this.escapeRegex(sourceFolder)}(\\/[^'"]*['"]\\s*\\))`,
|
||||
'g'
|
||||
);
|
||||
result = result.replace(requirePattern, `$1$2${destFolder}$3`);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape special regex characters in a string
|
||||
*/
|
||||
private escapeRegex(str: string): string {
|
||||
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to rewrite import paths in output directories
|
||||
*
|
||||
* @param globPatterns - Map of source patterns to destination directories
|
||||
* @param outputDirs - List of output directories to process
|
||||
* @returns Total number of files modified
|
||||
*/
|
||||
export async function rewriteImportPaths(
|
||||
globPatterns: Record<string, string>,
|
||||
outputDirs: string[]
|
||||
): Promise<number> {
|
||||
const rewriter = TsPathRewriter.fromGlobPatterns(globPatterns);
|
||||
let totalModified = 0;
|
||||
|
||||
for (const dir of outputDirs) {
|
||||
totalModified += await rewriter.rewriteDirectory(dir);
|
||||
}
|
||||
|
||||
return totalModified;
|
||||
}
|
||||
1
ts/mod_pathrewrite/index.ts
Normal file
1
ts/mod_pathrewrite/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './classes.tspathrewriter.js';
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { TsPublishConfig } from '../mod_config/index.js';
|
||||
import { FsHelpers } from '../mod_fs/index.js';
|
||||
@@ -81,59 +80,40 @@ export class TsUnpacker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the unpack operation - flatten nested output directories
|
||||
* Returns true if unpacking was performed, false if skipped
|
||||
* Perform the unpack operation - flatten nested output directories.
|
||||
*
|
||||
* Renames the nested directory to a temp location, removes the dest dir,
|
||||
* then renames the temp dir back as dest. Uses only rename operations
|
||||
* which are atomic at the kernel level.
|
||||
*
|
||||
* Returns true if unpacking was performed, false if skipped.
|
||||
*/
|
||||
public async unpack(): Promise<boolean> {
|
||||
// Check if we should unpack based on config
|
||||
if (!(await this.shouldUnpack())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if nested structure exists
|
||||
if (!(await this.detectNesting())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const nestedPath = this.getNestedPath();
|
||||
const tempPath = this.destDir + '.__unpack_temp__';
|
||||
|
||||
// Delete sibling folders (not the source folder)
|
||||
await this.removeSiblingDirectories();
|
||||
// Step 1: Clean up any leftover temp dir from a previous failed run
|
||||
await FsHelpers.removeDirectory(tempPath);
|
||||
|
||||
// Move contents from nested folder up
|
||||
await this.moveNestedContentsUp();
|
||||
// Step 2: Rename nested → temp
|
||||
await FsHelpers.move(nestedPath, tempPath);
|
||||
|
||||
// Remove empty nested folder
|
||||
await FsHelpers.removeEmptyDirectory(nestedPath);
|
||||
// Step 3: Remove dest dir (now contains only sibling folders)
|
||||
await FsHelpers.removeDirectory(this.destDir);
|
||||
|
||||
// Step 4: Rename temp → dest
|
||||
await FsHelpers.move(tempPath, this.destDir);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove sibling directories in the destination folder
|
||||
* (directories other than the source folder being unpacked)
|
||||
*/
|
||||
private async removeSiblingDirectories(): Promise<void> {
|
||||
const entries = await FsHelpers.listDirectory(this.destDir);
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory && entry.name !== this.sourceFolderName) {
|
||||
await FsHelpers.removeDirectory(path.join(this.destDir, entry.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move contents from the nested folder up to the destination directory
|
||||
*/
|
||||
private async moveNestedContentsUp(): Promise<void> {
|
||||
const nestedPath = this.getNestedPath();
|
||||
const entries = await FsHelpers.listDirectory(nestedPath);
|
||||
for (const entry of entries) {
|
||||
const src = path.join(nestedPath, entry.name);
|
||||
const dest = path.join(this.destDir, entry.name);
|
||||
await FsHelpers.move(src, dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user