Compare commits

..

24 Commits

Author SHA1 Message Date
88c0601c03 2.5.1 2025-05-15 14:07:26 +00:00
9645f27942 fix(commitinfo): Update commit information and metadata to synchronize release data 2025-05-15 14:07:26 +00:00
b73aa4f21f 2.5.0 2025-05-15 13:53:08 +00:00
d9d6878a9f feat(cli): Enhance type checking in CLI by adding default file pattern handling 2025-05-15 13:53:08 +00:00
30506da84c 2.4.1 2025-05-15 09:55:29 +00:00
52b4d8f944 fix(cli): Improve TS folder compilation order display in CLI 2025-05-15 09:55:29 +00:00
57d2726f6b 2.4.0 2025-05-15 09:31:57 +00:00
960fc5f213 feat(cli): Add new check command for type checking and update compiler options handling 2025-05-15 09:31:57 +00:00
6d68f35a9a 2.3.2 2025-03-20 15:30:52 +00:00
0378b9feca fix(compileGlobStringObject): Fix duplicate file outputs in glob pattern processing 2025-03-20 15:30:52 +00:00
5ecf4b7125 2.3.1 2025-03-20 15:20:27 +00:00
2aa6348cdd fix(compiler): Refactor compiler implementation with consolidated TsBuild class and improved diagnostics handling 2025-03-20 15:20:27 +00:00
9f42670865 2.3.0 2025-03-20 15:16:02 +00:00
6cc9f41bd2 feat(cli): Add emitcheck command to validate TS file emission without generating output 2025-03-20 15:16:02 +00:00
5d32ac85e0 2.2.7 2025-03-17 10:27:36 +00:00
4f2ac6922a fix(compiler): Improve diagnostic checking and error handling in the TypeScript compiler integration 2025-03-17 10:27:36 +00:00
31834e0b3e 2.2.6 2025-03-04 23:13:33 +00:00
c6c94866bb fix(package): Fix repository URL in package.json 2025-03-04 23:13:33 +00:00
09a648b435 2.2.5 2025-03-04 23:00:51 +00:00
065e0baaf7 fix(package.json): Update repository URLs in package metadata. 2025-03-04 23:00:51 +00:00
89d32617ce 2.2.4 2025-03-04 19:41:37 +00:00
094702b917 fix(core): Fix compiler logic to remove duplicate compiled files and improve glob pattern handling. 2025-03-04 19:41:37 +00:00
c2aec98da3 2.2.3 2025-03-04 19:41:05 +00:00
289421206c fix(exports): Fixed duplicate file compilation in compileGlobStringObject, fixes 2025-03-04 19:41:05 +00:00
11 changed files with 1117 additions and 233 deletions

1
.gitignore vendored

@ -18,3 +18,4 @@ dist/
dist_*/ dist_*/
# custom # custom
.claude

@ -1,5 +1,85 @@
# Changelog # Changelog
## 2025-05-15 - 2.5.1 - fix(commitinfo)
Update commit information and metadata to synchronize release data
- Regenerated the commitinfo file with current version details
- Maintained existing functionality with no functional code changes
## 2025-05-15 - 2.5.0 - feat(cli)
Enhance type checking in CLI by adding default file pattern handling
- When no TypeScript file or glob pattern is provided, the CLI now performs a default type checking sequence.
- First checks 'ts/**/*' files with standard options, then checks 'test/**/*' files with skiplibcheck enabled.
- Improved logging to indicate file discovery and check results, ensuring clear feedback for users.
## 2025-05-15 - 2.4.1 - fix(cli)
Improve TS folder compilation order display in CLI
- Refactor folder compilation output to use a bordered, tabular format with order numbering
- Enhance readability of TS folder compilation plan in the CLI output
## 2025-05-15 - 2.4.0 - feat(cli)
Add new 'check' command for type checking and update compiler options handling
- Introduced a new 'check' command to verify TypeScript files without emitting output
- Updated CLI error messages and logging for better clarity
- Replaced '--allowimplicitany' flag with '--disallowimplicitany' to reflect new default behavior
- Modified compiler options default settings (noImplicitAny now set to false) for more flexible type handling
- Refined diagnostic output in tsbuild class for improved error reporting
- Updated .gitignore to exclude the .claude file
- Enhanced documentation in readme and implementation plan files
## 2025-03-20 - 2.3.2 - fix(compileGlobStringObject)
Fix duplicate file outputs in glob pattern processing
- Removed duplicate concatenation of compiled files in compileGlobStringObject
- Ensured unique file paths are used during TypeScript compilation
## 2025-03-20 - 2.3.1 - fix(compiler)
Refactor compiler implementation with consolidated TsBuild class and improved diagnostics handling
- Removed legacy tsbuild.classes.compiler.ts and introduced tsbuild.classes.tsbuild.ts
- Unified compiler options merging, reading tsconfig.json, and diagnostics reporting within the TsBuild class
- Updated exports to reference the new compiler class implementation for backward compatibility
## 2025-03-20 - 2.3.0 - feat(cli)
Add emitcheck command to validate TS file emission without generating output
- Implemented the emitcheck CLI command to allow checking if TypeScript files can be emitted without producing files
- Updated tsbuild.classes.compiler.ts to include the emitCheck function
- Enhanced documentation in readme.md to describe the new emitcheck usage with examples
## 2025-03-17 - 2.2.7 - fix(compiler)
Improve diagnostic checking and error handling in the TypeScript compiler integration
- Added pre-emit diagnostics check to log errors before emitting
- Process exits on encountering pre-emit errors to ensure build correctness
- Enhanced logging for emit diagnostics to aid debugging
## 2025-03-04 - 2.2.6 - fix(package)
Fix repository URL in package.json
- Updated repository URL in package.json to point to the correct Git repository.
## 2025-03-04 - 2.2.5 - fix(package.json)
Update repository URLs in package metadata.
- Corrected the 'bugs.url' and 'homepage' fields with the accurate URLs.
## 2025-03-04 - 2.2.4 - fix(core)
Fix compiler logic to remove duplicate compiled files and improve glob pattern handling.
- Resolved an issue causing duplicate file compilations when using compileGlobStringObject.
- Improved handling of glob patterns to ensure accurate compilation paths.
## 2025-03-04 - 2.2.3 - fix(exports)
Fixed duplicate file compilation in compileGlobStringObject
- Enhanced type safety checks for glob pattern compilation keys.
- Ensured file list transformations include type checks.
- Fixed issue with duplicate compilation results in compileGlobStringObject.
## 2025-01-28 - 2.2.2 - fix(ci) ## 2025-01-28 - 2.2.2 - fix(ci)
Remove GitLab CI configuration Remove GitLab CI configuration

@ -9,9 +9,23 @@
"githost": "gitlab.com", "githost": "gitlab.com",
"gitscope": "gitzone", "gitscope": "gitzone",
"gitrepo": "tsbuild", "gitrepo": "tsbuild",
"description": "TypeScript nightly to easily make use of latest features", "description": "A tool for compiling TypeScript files using the latest nightly features, offering flexible APIs and a CLI for streamlined development.",
"npmPackagename": "@gitzone/tsbuild", "npmPackagename": "@gitzone/tsbuild",
"license": "MIT" "license": "MIT",
} "keywords": [
"TypeScript",
"compilation",
"nightly features",
"CLI tool",
"file compilation",
"glob patterns",
"compiler options",
"development",
"API"
]
}
},
"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"
} }
} }

@ -1,8 +1,8 @@
{ {
"name": "@git.zone/tsbuild", "name": "@git.zone/tsbuild",
"version": "2.2.2", "version": "2.5.1",
"private": false, "private": false,
"description": "TypeScript nightly to easily make use of latest features", "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", "main": "dist_ts/index.js",
"typings": "dist_ts/index.d.ts", "typings": "dist_ts/index.d.ts",
"type": "module", "type": "module",
@ -11,22 +11,30 @@
}, },
"scripts": { "scripts": {
"test": "tsrun test/test.ts", "test": "tsrun test/test.ts",
"build": "node cli.ts.js --web --allowimplicitany", "build": "node cli.ts.js --web",
"buildDocs": "tsdoc" "buildDocs": "tsdoc"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+ssh://git@gitlab.com/pushrocks/tsn.git" "url": "https://code.foss.global/git.zone/tsbuild.git"
}, },
"keywords": [ "keywords": [
"TypeScript" "TypeScript",
"compilation",
"nightly features",
"CLI tool",
"file compilation",
"glob patterns",
"compiler options",
"development",
"API"
], ],
"author": "Lossless GmbH", "author": "Lossless GmbH",
"license": "MIT", "license": "MIT",
"bugs": { "bugs": {
"url": "https://gitlab.com/pushrocks/tsn/issues" "url": "https://code.foss.global/git.zone/tsbuild/issues"
}, },
"homepage": "https://gitlab.com/pushrocks/tsn#README", "homepage": "https://code.foss.global/git.zone/tsbuild#README",
"dependencies": { "dependencies": {
"@git.zone/tspublish": "^1.9.1", "@git.zone/tspublish": "^1.9.1",
"@push.rocks/early": "^4.0.4", "@push.rocks/early": "^4.0.4",
@ -57,5 +65,6 @@
], ],
"browserslist": [ "browserslist": [
"last 1 chrome versions" "last 1 chrome versions"
] ],
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
} }

305
readme.md

@ -1,84 +1,241 @@
# @gitzone/tsbuild # @git.zone/tsbuild
TypeScript nightly to easily make use of latest features
## Availabililty and Links A flexible TypeScript compiler that leverages the latest TypeScript features to streamline your build process.
* [npmjs.org (npm package)](https://www.npmjs.com/package/@gitzone/tsbuild)
* [gitlab.com (source)](https://gitlab.com/gitzone/tsbuild)
* [github.com (source mirror)](https://github.com/gitzone/tsbuild)
* [docs (typedoc)](https://gitzone.gitlab.io/tsbuild/)
## Status for master ## Install
Status Category | Status Badge Add `@git.zone/tsbuild` to your project using npm or yarn:
-- | --
GitLab Pipelines | [![pipeline status](https://gitlab.com/gitzone/tsbuild/badges/master/pipeline.svg)](https://lossless.cloud)
GitLab Pipline Test Coverage | [![coverage report](https://gitlab.com/gitzone/tsbuild/badges/master/coverage.svg)](https://lossless.cloud)
npm | [![npm downloads per month](https://badgen.net/npm/dy/@gitzone/tsbuild)](https://lossless.cloud)
Snyk | [![Known Vulnerabilities](https://badgen.net/snyk/gitzone/tsbuild)](https://lossless.cloud)
TypeScript Support | [![TypeScript](https://badgen.net/badge/TypeScript/>=%203.x/blue?icon=typescript)](https://lossless.cloud)
node Support | [![node](https://img.shields.io/badge/node->=%2010.x.x-blue.svg)](https://nodejs.org/dist/latest-v10.x/docs/api/)
Code Style | [![Code Style](https://badgen.net/badge/style/prettier/purple)](https://lossless.cloud)
PackagePhobia (total standalone install weight) | [![PackagePhobia](https://badgen.net/packagephobia/install/@gitzone/tsbuild)](https://lossless.cloud)
PackagePhobia (package size on registry) | [![PackagePhobia](https://badgen.net/packagephobia/publish/@gitzone/tsbuild)](https://lossless.cloud)
BundlePhobia (total size when bundled) | [![BundlePhobia](https://badgen.net/bundlephobia/minzip/@gitzone/tsbuild)](https://lossless.cloud)
## Usage ```bash
npm install @git.zone/tsbuild
Tsn uses the **next** tagged npm version of typescript
```typescript
import * as tsn from 'tsn';
let myGlobStringObject = {
'./myTsFolder/**/*.ts': './myDestinationFolder/',
'./someOtherTsFolder/**/*.ts': './myOtherDestinationFolder/',
};
let tsOptions = {
target: tsn.ScriptTarget.ES2015,
module: tsn.ModuleKind.CommonJS,
};
/*
note: since this only works in code, here are the target numbers
enum ScriptTarget {
ES3 = 0,
ES5 = 1,
ES2015 = 2,
ES2016 = 3,
ES2017 = 4,
ESNext = 5,
Latest = 5,
}
and here are the module kinds
enum ModuleKind {
None = 0,
CommonJS = 1,
AMD = 2,
UMD = 3,
System = 4,
ES2015 = 5,
}
*/
let myCwd = process.cwd();
tsn.compileGlobStringObject(
myGlobStringObject, // the glob string object describing from where to compile what to where
tsOptions, // the options for TypeScript
myCwd // a custom cwd, optional, defaults to process.cwd()
);
``` ```
[![npm](https://push.rocks/assets/repo-header.svg)](https://push.rocks) or
## Contribution ```bash
yarn add @git.zone/tsbuild
```
We are always happy for code contributions. If you are not the code contributing type that is ok. Still, maintaining Open Source repositories takes considerable time and thought. If you like the quality of what we do and our modules are useful to you we would appreciate a little monthly contribution: You can [contribute one time](https://lossless.link/contribute-onetime) or [contribute monthly](https://lossless.link/contribute). :) ## Key Features
For further information read the linked docs at the top of this readme. - Utilize the latest TypeScript features
- Flexible API for customized compilation processes
- Intuitive CLI for common build tasks
- Support for glob patterns to easily target files
- Ordered compilation to respect module dependencies
## Legal ## API Reference
> MIT licensed | **©** [Task Venture Capital GmbH](https://task.vc)
| By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy) ### Core Compilation Functions
#### `compileFileArray(fileStringArrayArg: string[], compilerOptionsArg?: CompilerOptions, argvArg?: any): Promise<any[]>`
Compiles an array of TypeScript files with customizable compiler options.
```typescript
import { compileFileArray } from '@git.zone/tsbuild';
const files = [
'./src/file1.ts',
'./src/file2.ts',
];
const options = {
target: "ES2020",
module: "CommonJS"
};
compileFileArray(files, options)
.then(compiledFiles => {
console.log('Compiled Files:', compiledFiles);
})
.catch(error => {
console.error('Compilation Error:', error);
});
```
#### `compileGlobStringObject(globStringObjectArg: Record<string, string>, tsOptionsArg?: CompilerOptions, cwdArg?: string, argvArg?: any): Promise<any[]>`
Compiles files matching glob patterns to specified output directories.
```typescript
import { compileGlobStringObject } from '@git.zone/tsbuild';
const globPattern = {
'./src/**/*.ts': './dist',
};
const compilerOptions = {
target: "ESNext",
module: "ESNext",
};
compileGlobStringObject(globPattern, compilerOptions)
.then(compiledFiles => {
console.log('Compilation complete:', compiledFiles);
})
.catch(error => {
console.error('Error during compilation:', error);
});
```
## Command Line Interface
The CLI provides convenient commands for common compilation tasks.
### Standard Command
Compiles all TypeScript files in the `ts/` directory to the `dist_ts` directory:
```bash
npx tsbuild
```
### Custom Command
Compile specific directories to corresponding `dist_` directories:
```bash
npx tsbuild custom <Dir1> <Dir2> ...
```
Example: `npx tsbuild custom src utils` compiles:
- `./src/**/*.ts``./dist_src`
- `./utils/**/*.ts``./dist_utils`
### TSFolders Command
Compiles TypeScript folders in a specific order based on dependencies:
```bash
npx tsbuild tsfolders
```
This command:
1. Identifies all folders starting with `ts` in the current directory
2. Prioritizes `ts_interfaces` and `ts_shared` to be compiled first
3. Orders other folders based on the `order` property in their `tspublish.json` files (if available)
4. Compiles each folder to its corresponding `dist_` folder
Example compilation order output:
```
compiling in this order:
[ 'ts_interfaces', 'ts_shared', 'ts_core', 'ts_utils', 'ts_modules' ]
```
### EmitCheck Command
Checks if TypeScript files can be emitted without actually emitting them:
```bash
npx tsbuild emitcheck <file_or_glob_pattern> [additional_patterns ...]
```
This command:
1. Performs type checking on the specified TypeScript file(s)
2. Supports both direct file paths and glob patterns
3. Reports any errors that would prevent successful compilation
4. Exits with code 0 if all files can be emitted, or 1 if any cannot
5. Doesn't produce any output files
Example usage with specific files:
```bash
npx tsbuild emitcheck src/main.ts src/utils.ts
```
Example usage with glob patterns:
```bash
npx tsbuild emitcheck "src/**/*.ts" "test/**/*.ts"
```
### Check Command
Performs type checking on TypeScript files specified by glob patterns without emitting them:
```bash
npx tsbuild check <glob_pattern> [additional_patterns ...]
```
This command:
1. Efficiently type checks TypeScript files matching the given glob patterns
2. Supports multiple glob patterns and direct file paths
3. Reports any type errors found in the matched files
4. Exits with code 0 if all files pass type checking, or 1 if any have errors
5. Doesn't produce any output files
Example usage:
```bash
npx tsbuild check ts/**/*
```
Example usage with multiple patterns:
```bash
npx tsbuild check "src/**/*.ts" "test/**/*.ts"
```
## Compiler Options
Additional flags can be passed to any command to modify the compilation behavior:
- `--skiplibcheck`: Skip type checking of declaration files (shows warning)
- `--disallowimplicitany`: Disallow variables to be implicitly typed as `any` (implicit any is allowed by default)
- `--commonjs`: Use CommonJS module format instead of ESNext
Example:
```bash
npx tsbuild --skiplibcheck --disallowimplicitany
```
## Default Compiler Options
By default, `@git.zone/tsbuild` uses the following compiler options:
```typescript
{
declaration: true,
emitDecoratorMetadata: true,
experimentalDecorators: true,
inlineSourceMap: true,
noEmitOnError: true,
outDir: 'dist_ts/',
module: ModuleKind.NodeNext,
target: ScriptTarget.ESNext,
moduleResolution: ModuleResolutionKind.NodeNext,
lib: ['lib.dom.d.ts', 'lib.es2022.d.ts'],
noImplicitAny: false, // Now allowing implicit any by default
esModuleInterop: true,
useDefineForClassFields: false,
verbatimModuleSyntax: true,
baseUrl: './',
}
```
These options can be overridden by providing a custom `CompilerOptions` object.
## Path Resolution
The package automatically detects and applies path mappings from your `tsconfig.json`. When it finds path mappings, it adjusts them to work with the compiled output by replacing `./ts_` with `./dist_ts_` in path aliases.
## Notes and Best Practices
- Each glob pattern compilation runs in its own pass, which may cause duplicate error messages if shared files are included in multiple patterns
- Use the `--skiplibcheck` option cautiously as it will pause for 5 seconds with a warning before continuing
- If you need different output configurations for different file sets, use multiple calls to `compileGlobStringObject`
## 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.
**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.
### 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.
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.

45
readme.plan.md Normal file

@ -0,0 +1,45 @@
# Implementation Plan for tsbuild `check` Command
## Overview
Add a new `check` command to tsbuild that allows checking TypeScript files against a glob pattern without emitting them, similar to running the TypeScript compiler with the `--noEmit` flag.
## Implementation Steps
1. **Reread CLAUDE.md** to ensure we follow project guidelines
2. **Extend TsBuild Class**
- The existing `TsBuild` class already has a `checkEmit()` method
- We can leverage this method for our implementation
3. **Implement Check Command in CLI**
- Add a new `check` command to `tsbuild.cli.ts`
- Command should accept glob patterns as arguments
- Process glob patterns to find matching TypeScript files
- Use the `TsBuild` class to check the files without emitting
4. **Update Exports**
- Ensure any new functionality is properly exported
5. **Testing**
- Test the command with various glob patterns
- Verify error reporting works correctly
## Differences from Existing `emitcheck` Command
The `emitcheck` command already exists and checks specific files without emitting. Our new `check` command will:
- Be designed specifically for checking files against glob patterns
- Use a simpler, more intuitive command name
- Potentially add additional benefits (like summary statistics of checked files)
## Example Usage
Once implemented, the command would work like this:
```bash
npx tsbuild check ts/**/*
npx tsbuild check "src/**/*.ts" "test/**/*.ts"
```
## Expected Output
The command should:
- Report any TypeScript errors in the matched files
- Provide a count of files checked and any errors found
- Exit with code 0 if successful, or 1 if errors are found

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@git.zone/tsbuild', name: '@git.zone/tsbuild',
version: '2.2.2', version: '2.5.1',
description: 'TypeScript nightly to easily make use of latest features' description: 'A tool for compiling TypeScript files using the latest nightly features, offering flexible APIs and a CLI for streamlined development.'
} }

@ -1,128 +0,0 @@
// import all the stuff we need
import * as plugins from './plugins.js';
import * as paths from './paths.js';
import type { CompilerOptions, ScriptTarget, ModuleKind } from './tsbuild.exports.js';
/**
* the default typescript compilerOptions
*/
export const compilerOptionsDefault: CompilerOptions = {
declaration: true,
emitDecoratorMetadata: true,
experimentalDecorators: true,
inlineSourceMap: true,
noEmitOnError: true,
outDir: 'dist_ts/',
module: plugins.typescript.ModuleKind.NodeNext,
target: plugins.typescript.ScriptTarget.ESNext,
moduleResolution: plugins.typescript.ModuleResolutionKind.NodeNext,
lib: ['lib.dom.d.ts', 'lib.es2022.d.ts'],
noImplicitAny: true,
esModuleInterop: true,
useDefineForClassFields: false,
verbatimModuleSyntax: true,
baseUrl: './',
};
/**
* merges compilerOptions with the default compiler options
*/
export const mergeCompilerOptions = (
customTsOptions: CompilerOptions,
argvArg?: any
): CompilerOptions => {
// create merged options
const mergedOptions: CompilerOptions = {
...compilerOptionsDefault,
...customTsOptions,
...(argvArg && argvArg.skiplibcheck
? {
skipLibCheck: true,
}
: {}),
...(argvArg && argvArg.allowimplicitany
? {
noImplicitAny: false,
}
: {}),
...(argvArg && argvArg.commonjs
? {
module: plugins.typescript.ModuleKind.CommonJS,
moduleResolution: plugins.typescript.ModuleResolutionKind.NodeJs,
}
: {}),
...(() => {
const returnObject: CompilerOptions = {};
console.log(`looking at tsconfig.json at ${paths.cwd}`);
const tsconfig = plugins.smartfile.fs.toObjectSync(plugins.path.join(paths.cwd, 'tsconfig.json'));
if (tsconfig && tsconfig.compilerOptions && tsconfig.compilerOptions.baseUrl) {
console.log('baseUrl found in tsconfig.json');
returnObject.baseUrl = tsconfig.compilerOptions.baseUrl;
}
if (tsconfig && tsconfig.compilerOptions && tsconfig.compilerOptions.paths) {
console.log('paths found in tsconfig.json');
returnObject.paths = tsconfig.compilerOptions.paths;
for (const path of Object.keys(tsconfig.compilerOptions.paths)) {
returnObject.paths[path][0] = returnObject.paths[path][0].replace('./ts_', './dist_ts_');
}
}
return returnObject;
})(),
};
console.log(mergedOptions);
return mergedOptions;
};
/**
* the internal main compiler function that compiles the files
*/
export const compiler = async (
fileNames: string[],
options: plugins.typescript.CompilerOptions,
argvArg?: any
): Promise<any[]> => {
if (options.skipLibCheck) {
console.log('? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?');
console.log('You are skipping libcheck... Is that really wanted?');
console.log('continuing in 5 seconds...');
console.log('? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?');
await plugins.smartdelay.delayFor(5000);
}
console.log(`Compiling ${fileNames.length} files...`);
const done = plugins.smartpromise.defer<any[]>();
const program = plugins.typescript.createProgram(fileNames, options);
const emitResult = program.emit();
// implement check only
/*let emitResult = program.emit(undefined,(args) => {
console.log(args)
});*/
const allDiagnostics = plugins.typescript
.getPreEmitDiagnostics(program)
.concat(emitResult.diagnostics);
allDiagnostics.forEach((diagnostic) => {
if (diagnostic.file) {
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!);
const message = plugins.typescript.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
console.log(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
} else {
console.log(
`${plugins.typescript.flattenDiagnosticMessageText(diagnostic.messageText, '\n')}`
);
}
});
const exitCode = emitResult.emitSkipped ? 1 : 0;
if (exitCode === 0) {
console.log('TypeScript emit succeeded!');
done.resolve(emitResult.emittedFiles);
} else {
console.error('TypeScript emit failed. Please investigate!');
process.exit(exitCode);
}
return done.promise;
};

@ -0,0 +1,450 @@
// import all the stuff we need
import * as plugins from './plugins.js';
import * as paths from './paths.js';
import type { CompilerOptions, ScriptTarget, ModuleKind } from './tsbuild.exports.js';
/**
* Default compiler options for TypeScript compilation
*/
export const compilerOptionsDefault: CompilerOptions = {
declaration: true,
emitDecoratorMetadata: true,
experimentalDecorators: true,
inlineSourceMap: true,
noEmitOnError: true,
outDir: 'dist_ts/',
module: plugins.typescript.ModuleKind.NodeNext,
target: plugins.typescript.ScriptTarget.ESNext,
moduleResolution: plugins.typescript.ModuleResolutionKind.NodeNext,
lib: ['lib.dom.d.ts', 'lib.es2022.d.ts'],
noImplicitAny: false, // Allow implicit any by default
esModuleInterop: true,
useDefineForClassFields: false,
verbatimModuleSyntax: true,
baseUrl: './',
};
/**
* TsBuild class for handling TypeScript compilation
*/
export class TsBuild {
private fileNames: string[] = [];
private options: plugins.typescript.CompilerOptions;
private argvArg?: any;
/**
* Create a new TsBuild instance
*/
constructor(
fileNames: string[] = [],
customOptions: CompilerOptions = {},
argvArg?: any
) {
this.fileNames = fileNames;
this.argvArg = argvArg;
this.options = this.mergeCompilerOptions(customOptions, argvArg);
}
/**
* Helper function to read and process tsconfig.json
*/
private getTsConfigOptions(): CompilerOptions {
const tsconfig = plugins.smartfile.fs.toObjectSync(plugins.path.join(paths.cwd, 'tsconfig.json'));
const returnObject: CompilerOptions = {};
if (!tsconfig || !tsconfig.compilerOptions) {
return returnObject;
}
// Process baseUrl
if (tsconfig.compilerOptions.baseUrl) {
returnObject.baseUrl = tsconfig.compilerOptions.baseUrl;
}
// Process paths
if (tsconfig.compilerOptions.paths) {
returnObject.paths = { ...tsconfig.compilerOptions.paths };
for (const path of Object.keys(returnObject.paths)) {
if (Array.isArray(returnObject.paths[path]) && returnObject.paths[path].length > 0) {
returnObject.paths[path][0] = returnObject.paths[path][0].replace('./ts_', './dist_ts_');
}
}
}
// Process target
if (tsconfig.compilerOptions.target) {
if (typeof tsconfig.compilerOptions.target === 'string') {
const targetKey = tsconfig.compilerOptions.target.toUpperCase();
if (targetKey in plugins.typescript.ScriptTarget) {
returnObject.target = plugins.typescript.ScriptTarget[targetKey as keyof typeof plugins.typescript.ScriptTarget];
}
}
}
// Process module
if (tsconfig.compilerOptions.module) {
if (typeof tsconfig.compilerOptions.module === 'string') {
const moduleKey = tsconfig.compilerOptions.module.toUpperCase();
if (moduleKey in plugins.typescript.ModuleKind) {
returnObject.module = plugins.typescript.ModuleKind[moduleKey as keyof typeof plugins.typescript.ModuleKind];
} else if (moduleKey === 'NODENEXT') {
returnObject.module = plugins.typescript.ModuleKind.NodeNext;
}
}
}
// Process moduleResolution
if (tsconfig.compilerOptions.moduleResolution) {
if (typeof tsconfig.compilerOptions.moduleResolution === 'string') {
const moduleResolutionKey = tsconfig.compilerOptions.moduleResolution.toUpperCase();
if (moduleResolutionKey in plugins.typescript.ModuleResolutionKind) {
returnObject.moduleResolution = plugins.typescript.ModuleResolutionKind[
moduleResolutionKey as keyof typeof plugins.typescript.ModuleResolutionKind
];
} else if (moduleResolutionKey === 'NODENEXT') {
returnObject.moduleResolution = plugins.typescript.ModuleResolutionKind.NodeNext;
}
}
}
// Copy boolean options directly
const booleanOptions = [
'experimentalDecorators', 'useDefineForClassFields',
'esModuleInterop', 'verbatimModuleSyntax'
];
for (const option of booleanOptions) {
if (option in tsconfig.compilerOptions) {
(returnObject as any)[option] = (tsconfig.compilerOptions as any)[option];
}
}
return returnObject;
}
/**
* Process command line arguments and return applicable compiler options
*/
private getCommandLineOptions(argvArg?: any): CompilerOptions {
if (!argvArg) return {};
const options: CompilerOptions = {};
if (argvArg.skiplibcheck) {
options.skipLibCheck = true;
}
// Changed behavior: --disallowimplicitany instead of --allowimplicitany
if (argvArg.disallowimplicitany) {
options.noImplicitAny = true;
}
if (argvArg.commonjs) {
options.module = plugins.typescript.ModuleKind.CommonJS;
options.moduleResolution = plugins.typescript.ModuleResolutionKind.NodeJs;
}
return options;
}
/**
* Merges compilerOptions with the default compiler options
*/
public mergeCompilerOptions(
customTsOptions: CompilerOptions = {},
argvArg?: any
): CompilerOptions {
// create merged options
const mergedOptions: CompilerOptions = {
...compilerOptionsDefault,
...customTsOptions,
...this.getCommandLineOptions(argvArg),
...this.getTsConfigOptions(),
};
return mergedOptions;
}
/**
* Helper function to handle and log TypeScript diagnostics
*/
private handleDiagnostics(diagnostics: readonly plugins.typescript.Diagnostic[]): boolean {
if (diagnostics.length === 0) {
return false;
}
// Group errors by file for better readability
const errorsByFile: Record<string, plugins.typescript.Diagnostic[]> = {};
const generalErrors: plugins.typescript.Diagnostic[] = [];
// Categorize diagnostics
diagnostics.forEach((diagnostic) => {
if (diagnostic.file) {
const fileName = diagnostic.file.fileName;
if (!errorsByFile[fileName]) {
errorsByFile[fileName] = [];
}
errorsByFile[fileName].push(diagnostic);
} else {
generalErrors.push(diagnostic);
}
});
// Print error summary header
const totalErrorCount = diagnostics.length;
const fileCount = Object.keys(errorsByFile).length;
console.log('\n' + '='.repeat(80));
console.log(`❌ Found ${totalErrorCount} error${totalErrorCount !== 1 ? 's' : ''} in ${fileCount} file${fileCount !== 1 ? 's' : ''}:`);
console.log('='.repeat(80));
// Color codes for error formatting
const colors = {
reset: '\x1b[0m',
red: '\x1b[31m',
yellow: '\x1b[33m',
cyan: '\x1b[36m',
white: '\x1b[37m',
brightRed: '\x1b[91m'
};
// Print file-specific errors
Object.entries(errorsByFile).forEach(([fileName, fileErrors]) => {
// Show relative path if possible for cleaner output
const displayPath = fileName.replace(process.cwd(), '').replace(/^\//, '');
console.log(`\n${colors.cyan}File: ${displayPath} ${colors.yellow}(${fileErrors.length} error${fileErrors.length !== 1 ? 's' : ''})${colors.reset}`);
console.log('-'.repeat(80));
fileErrors.forEach((diagnostic) => {
if (diagnostic.file && diagnostic.start !== undefined) {
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
const message = plugins.typescript.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
const errorCode = diagnostic.code ? `TS${diagnostic.code}` : 'Error';
console.log(`${colors.white}Line ${line + 1}, Col ${character + 1}${colors.reset}: ${colors.brightRed}${errorCode}${colors.reset} - ${message}`);
// Try to show the code snippet if possible
try {
const lineContent = diagnostic.file.text.split('\n')[line];
if (lineContent) {
// Show the line of code
console.log(` ${lineContent.trimRight()}`);
// Show the error position indicator
const indicator = ' '.repeat(character) + `${colors.red}^${colors.reset}`;
console.log(` ${indicator}`);
}
} catch (e) {
// Failed to get source text, skip showing the code snippet
}
}
});
});
// Print general errors
if (generalErrors.length > 0) {
console.log(`\n${colors.yellow}General Errors:${colors.reset}`);
console.log('-'.repeat(80));
generalErrors.forEach((diagnostic) => {
const message = plugins.typescript.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
const errorCode = diagnostic.code ? `TS${diagnostic.code}` : 'Error';
console.log(`${colors.brightRed}${errorCode}${colors.reset}: ${message}`);
});
}
console.log('\n' + '='.repeat(80) + '\n');
return diagnostics.length > 0;
}
/**
* Creates a TypeScript program from file names and options
*/
private createProgram(
options: plugins.typescript.CompilerOptions = this.options
): plugins.typescript.Program {
return plugins.typescript.createProgram(this.fileNames, options);
}
/**
* Set file names to be compiled
*/
public setFileNames(fileNames: string[]): void {
this.fileNames = fileNames;
}
/**
* Set compiler options
*/
public setOptions(options: CompilerOptions): void {
this.options = { ...this.options, ...options };
}
/**
* The main compiler function that compiles the files
*/
public async compile(): Promise<any[]> {
if (this.options.skipLibCheck) {
console.log('\n⚠ WARNING ⚠️');
console.log('You are skipping libcheck... Is that really wanted?');
console.log('Continuing in 5 seconds...\n');
await plugins.smartdelay.delayFor(5000);
}
console.log(`🔨 Compiling ${this.fileNames.length} files...`);
const done = plugins.smartpromise.defer<any[]>();
const program = this.createProgram();
// Check for pre-emit diagnostics first
const preEmitDiagnostics = plugins.typescript.getPreEmitDiagnostics(program);
const hasPreEmitErrors = this.handleDiagnostics(preEmitDiagnostics);
// Only continue to emit phase if no pre-emit errors
if (hasPreEmitErrors) {
console.error('\n❌ TypeScript pre-emit checks failed. Please fix the issues listed above before proceeding.');
console.error(' Type errors must be resolved before the compiler can emit output files.\n');
process.exit(1);
}
// If no pre-emit errors, proceed with emit
const emitResult = program.emit();
const hasEmitErrors = this.handleDiagnostics(emitResult.diagnostics);
const exitCode = emitResult.emitSkipped ? 1 : 0;
if (exitCode === 0) {
console.log('\n✅ TypeScript emit succeeded!');
// Get count of emitted files by type
const jsFiles = emitResult.emittedFiles?.filter(f => f.endsWith('.js')).length || 0;
const dtsFiles = emitResult.emittedFiles?.filter(f => f.endsWith('.d.ts')).length || 0;
const mapFiles = emitResult.emittedFiles?.filter(f => f.endsWith('.map')).length || 0;
// If we have emitted files, show a summary
if (emitResult.emittedFiles && emitResult.emittedFiles.length > 0) {
console.log(` Generated ${emitResult.emittedFiles.length} files: ${jsFiles} .js, ${dtsFiles} .d.ts, ${mapFiles} source maps`);
}
done.resolve(emitResult.emittedFiles);
} else {
console.error('\n❌ TypeScript emit failed. Please investigate the errors listed above!');
console.error(' No output files have been generated.\n');
process.exit(exitCode);
}
return done.promise;
}
/**
* Function to check if files can be emitted without actually emitting them
*/
public async checkEmit(): Promise<boolean> {
const fileCount = this.fileNames.length;
console.log(`\n🔍 Checking if ${fileCount} file${fileCount !== 1 ? 's' : ''} can be emitted...`);
// Create a program with noEmit option
const program = this.createProgram({
...this.options,
noEmit: true
});
// Check for pre-emit diagnostics
const preEmitDiagnostics = plugins.typescript.getPreEmitDiagnostics(program);
const hasPreEmitErrors = this.handleDiagnostics(preEmitDiagnostics);
// Run the emit phase but with noEmit: true to check for emit errors without producing files
const emitResult = program.emit(undefined, undefined, undefined, true);
const hasEmitErrors = this.handleDiagnostics(emitResult.diagnostics);
const success = !hasPreEmitErrors && !hasEmitErrors && !emitResult.emitSkipped;
if (success) {
console.log('\n✅ TypeScript emit check passed! All files can be emitted successfully.');
console.log(` ${fileCount} file${fileCount !== 1 ? 's' : ''} ${fileCount !== 1 ? 'are' : 'is'} ready to be compiled.\n`);
} else {
console.error('\n❌ TypeScript emit check failed. Please fix the issues listed above.');
console.error(' The compilation cannot proceed until these errors are resolved.\n');
}
return success;
}
/**
* Function to check TypeScript files for type errors without emission
*/
public async checkTypes(): Promise<boolean> {
const fileCount = this.fileNames.length;
console.log(`\n🔍 Type checking ${fileCount} TypeScript file${fileCount !== 1 ? 's' : ''}...`);
// Create a program with noEmit option explicitly set
const program = this.createProgram({
...this.options,
noEmit: true
});
// Check for type errors
const diagnostics = plugins.typescript.getPreEmitDiagnostics(program);
const hasErrors = this.handleDiagnostics(diagnostics);
// Set success flag
const success = !hasErrors;
if (success) {
console.log('\n✅ TypeScript type check passed! No type errors found.');
console.log(` All ${fileCount} file${fileCount !== 1 ? 's' : ''} passed type checking successfully.\n`);
} else {
console.error('\n❌ TypeScript type check failed. Please fix the type errors listed above.');
console.error(' The type checker found issues that need to be resolved.\n');
}
return success;
}
}
/**
* Merges compilerOptions with the default compiler options (backward compatibility)
*/
export const mergeCompilerOptions = (
customTsOptions: CompilerOptions,
argvArg?: any
): CompilerOptions => {
const tsBuild = new TsBuild();
return tsBuild.mergeCompilerOptions(customTsOptions, argvArg);
};
/**
* The internal main compiler function that compiles the files (backward compatibility)
*/
export const compiler = async (
fileNames: string[],
options: plugins.typescript.CompilerOptions,
argvArg?: any
): Promise<any[]> => {
const tsBuild = new TsBuild(fileNames, options, argvArg);
return tsBuild.compile();
};
/**
* Function to check if a TypeScript file can be emitted without actually emitting it (backward compatibility)
*/
export const emitCheck = async (
fileNames: string[],
options: plugins.typescript.CompilerOptions = {},
argvArg?: any
): Promise<boolean> => {
const tsBuild = new TsBuild(fileNames, options, argvArg);
return tsBuild.checkEmit();
};
/**
* Function to check TypeScript files for type errors without emission (backward compatibility)
*/
export const checkTypes = async (
fileNames: string[],
options: plugins.typescript.CompilerOptions = {},
argvArg?: any
): Promise<boolean> => {
const tsBuild = new TsBuild(fileNames, options, argvArg);
return tsBuild.checkTypes();
};

@ -32,6 +32,90 @@ export const runCli = async () => {
await tsbuild.compileGlobStringObject(compilationCommandObject, {}, process.cwd(), argvArg); await tsbuild.compileGlobStringObject(compilationCommandObject, {}, process.cwd(), argvArg);
}); });
/**
* the emitcheck command checks if a TypeScript file can be emitted without actually emitting it
*/
tsbuildCli.addCommand('emitcheck').subscribe(async (argvArg) => {
const patterns = argvArg._.slice(1); // Remove the first element which is 'emitcheck'
if (patterns.length === 0) {
console.error('\n❌ Error: Please provide at least one TypeScript file path or glob pattern');
console.error(' Usage: tsbuild emitcheck <file_or_glob_pattern> [additional_patterns ...]\n');
console.error(' Example: tsbuild emitcheck "src/**/*.ts" "test/**/*.ts"\n');
process.exit(1);
}
const cwd = process.cwd();
let allFiles: string[] = [];
// Process each pattern - could be a direct file path or a glob pattern
for (const pattern of patterns) {
// Check if the pattern looks like a glob pattern
if (pattern.includes('*') || pattern.includes('{') || pattern.includes('?')) {
// Handle as glob pattern
console.log(`Processing glob pattern: ${pattern}`);
try {
const matchedFiles = await plugins.smartfile.fs.listFileTree(cwd, pattern);
// Ensure matchedFiles contains only strings
const stringMatchedFiles = Array.isArray(matchedFiles)
? matchedFiles.filter((item): item is string => typeof item === 'string')
: [];
if (stringMatchedFiles.length === 0) {
console.warn(`⚠️ Warning: No files matched the pattern '${pattern}'`);
} else {
console.log(`📂 Found ${stringMatchedFiles.length} files matching pattern '${pattern}'`);
// Transform to absolute paths
const absoluteMatchedFiles = plugins.smartpath.transform.toAbsolute(
stringMatchedFiles,
cwd
) as string[];
// Add to the list of all files to check
allFiles = allFiles.concat(absoluteMatchedFiles);
}
} catch (err) {
console.error(`❌ Error processing glob pattern '${pattern}': ${err}`);
}
} else {
// Handle as direct file path
const filePath = plugins.path.isAbsolute(pattern)
? pattern
: plugins.path.join(cwd, pattern);
try {
await plugins.smartfile.fs.fileExists(filePath);
allFiles.push(filePath);
} catch (err) {
console.error(`❌ Error: File not found: ${filePath}`);
process.exit(1);
}
}
}
// Filter to only TypeScript files
allFiles = allFiles.filter(file => file.endsWith('.ts') || file.endsWith('.tsx'));
if (allFiles.length === 0) {
console.error('\n❌ Error: No TypeScript files found to check');
console.error(' Please verify your file paths or glob patterns.\n');
process.exit(1);
}
console.log(`\n🔎 Found ${allFiles.length} TypeScript file${allFiles.length !== 1 ? 's' : ''} to check`);
// Process compiler options
const compilerOptions = tsbuild.mergeCompilerOptions({}, argvArg);
// Run emit check
const success = await tsbuild.emitCheck(allFiles, compilerOptions, argvArg);
// Exit with appropriate code
process.exit(success ? 0 : 1);
});
/** /**
* the custom command compiles any customDir to dist_customDir * the custom command compiles any customDir to dist_customDir
*/ */
@ -80,13 +164,164 @@ export const runCli = async () => {
const compilationCommandObject: { [key: string]: string } = {}; const compilationCommandObject: { [key: string]: string } = {};
console.log(`compiling in this order:`); const folderCount = sortedTsFolders.length;
console.log(sortedTsFolders); console.log(`\n📂 TypeScript Folder Compilation Plan (${folderCount} folder${folderCount !== 1 ? 's' : ''})`);
console.log('┌' + '─'.repeat(60) + '┐');
console.log('│ 🔄 Compilation Order │');
console.log('├' + '─'.repeat(60) + '┤');
sortedTsFolders.forEach((folder, index) => {
const prefix = index === folderCount - 1 ? '└─' : '├─';
const position = `${index + 1}/${folderCount}`;
console.log(`${prefix} ${position.padStart(5)} ${folder.padEnd(46)}`);
});
console.log('└' + '─'.repeat(60) + '┘\n');
for (const tsFolder of sortedTsFolders) { for (const tsFolder of sortedTsFolders) {
compilationCommandObject[`./${tsFolder}/**/*.ts`] = `./dist_${tsFolder}`; compilationCommandObject[`./${tsFolder}/**/*.ts`] = `./dist_${tsFolder}`;
} }
await tsbuild.compileGlobStringObject(compilationCommandObject, {}, process.cwd(), argvArg); await tsbuild.compileGlobStringObject(compilationCommandObject, {}, process.cwd(), argvArg);
}); });
/**
* the check command checks TypeScript files against a glob pattern without emitting them
*/
tsbuildCli.addCommand('check').subscribe(async (argvArg) => {
const patterns = argvArg._.slice(1); // Remove the first element which is 'check'
// If no patterns provided, default to checking ts/**/* and then test/**/*
if (patterns.length === 0) {
console.log('\n🔬 Running default type checking sequence...\n');
// First check ts/**/* without skiplibcheck
console.log('📂 Checking ts/**/* files...');
const tsFiles = await plugins.smartfile.fs.listFileTree(process.cwd(), 'ts/**/*.ts');
const tsTsFiles = Array.isArray(tsFiles)
? tsFiles.filter((item): item is string => typeof item === 'string')
: [];
if (tsTsFiles.length > 0) {
console.log(` Found ${tsTsFiles.length} TypeScript files in ts/`);
const tsAbsoluteFiles = plugins.smartpath.transform.toAbsolute(
tsTsFiles,
process.cwd()
) as string[];
const tsCompilerOptions = tsbuild.mergeCompilerOptions({}, argvArg);
const tsSuccess = await tsbuild.checkTypes(tsAbsoluteFiles, tsCompilerOptions, argvArg);
if (!tsSuccess) {
console.error('❌ Type checking failed for ts/**/*');
process.exit(1);
}
console.log('✅ Type checking passed for ts/**/*\n');
} else {
console.log(' No TypeScript files found in ts/\n');
}
// Then check test/**/* with skiplibcheck
console.log('📂 Checking test/**/* files with --skiplibcheck...');
const testFiles = await plugins.smartfile.fs.listFileTree(process.cwd(), 'test/**/*.ts');
const testTsFiles = Array.isArray(testFiles)
? testFiles.filter((item): item is string => typeof item === 'string')
: [];
if (testTsFiles.length > 0) {
console.log(` Found ${testTsFiles.length} TypeScript files in test/`);
const testAbsoluteFiles = plugins.smartpath.transform.toAbsolute(
testTsFiles,
process.cwd()
) as string[];
// Create new argvArg with skiplibcheck for test files
const testArgvArg = { ...argvArg, skiplibcheck: true };
const testCompilerOptions = tsbuild.mergeCompilerOptions({}, testArgvArg);
const testSuccess = await tsbuild.checkTypes(testAbsoluteFiles, testCompilerOptions, testArgvArg);
if (!testSuccess) {
console.error('❌ Type checking failed for test/**/*');
process.exit(1);
}
console.log('✅ Type checking passed for test/**/*\n');
} else {
console.log(' No TypeScript files found in test/\n');
}
console.log('✅ All default type checks passed!\n');
process.exit(0);
}
const cwd = process.cwd();
let allFiles: string[] = [];
// Process each pattern - could be a direct file path or a glob pattern
for (const pattern of patterns) {
// Check if the pattern looks like a glob pattern
if (pattern.includes('*') || pattern.includes('{') || pattern.includes('?')) {
// Handle as glob pattern
console.log(`Processing glob pattern: ${pattern}`);
try {
const matchedFiles = await plugins.smartfile.fs.listFileTree(cwd, pattern);
// Ensure matchedFiles contains only strings
const stringMatchedFiles = Array.isArray(matchedFiles)
? matchedFiles.filter((item): item is string => typeof item === 'string')
: [];
if (stringMatchedFiles.length === 0) {
console.warn(`⚠️ Warning: No files matched the pattern '${pattern}'`);
} else {
console.log(`📂 Found ${stringMatchedFiles.length} files matching pattern '${pattern}'`);
// Transform to absolute paths
const absoluteMatchedFiles = plugins.smartpath.transform.toAbsolute(
stringMatchedFiles,
cwd
) as string[];
// Add to the list of all files to check
allFiles = allFiles.concat(absoluteMatchedFiles);
}
} catch (err) {
console.error(`❌ Error processing glob pattern '${pattern}': ${err}`);
}
} else {
// Handle as direct file path
const filePath = plugins.path.isAbsolute(pattern)
? pattern
: plugins.path.join(cwd, pattern);
try {
await plugins.smartfile.fs.fileExists(filePath);
allFiles.push(filePath);
} catch (err) {
console.error(`❌ Error: File not found: ${filePath}`);
process.exit(1);
}
}
}
// Filter to only TypeScript files
allFiles = allFiles.filter(file => file.endsWith('.ts') || file.endsWith('.tsx'));
if (allFiles.length === 0) {
console.error('\n❌ Error: No TypeScript files found to check');
console.error(' Please verify your file paths or glob patterns.\n');
process.exit(1);
}
console.log(`\n🔎 Found ${allFiles.length} TypeScript file${allFiles.length !== 1 ? 's' : ''} to check`);
// Process compiler options
const compilerOptions = tsbuild.mergeCompilerOptions({}, argvArg);
// Run type check without emitting
const success = await tsbuild.checkTypes(allFiles, compilerOptions, argvArg);
// Exit with appropriate code
process.exit(success ? 0 : 1);
});
tsbuildCli.startParse(); tsbuildCli.startParse();
}; };

@ -1,10 +1,10 @@
import * as plugins from './plugins.js'; import * as plugins from './plugins.js';
import type { CompilerOptions, ScriptTarget, ModuleKind } from 'typescript'; import type { CompilerOptions, ScriptTarget, ModuleKind } from 'typescript';
import { compiler, mergeCompilerOptions } from './tsbuild.classes.compiler.js'; import { compiler, mergeCompilerOptions, emitCheck, checkTypes } from './tsbuild.classes.tsbuild.js';
export type { CompilerOptions, ScriptTarget, ModuleKind }; export type { CompilerOptions, ScriptTarget, ModuleKind };
export * from './tsbuild.classes.compiler.js'; export * from './tsbuild.classes.tsbuild.js';
/** /**
* compile am array of absolute file paths * compile am array of absolute file paths
@ -25,35 +25,56 @@ export let compileFileArray = (
* } * }
*/ */
export let compileGlobStringObject = async ( export let compileGlobStringObject = async (
globStringObjectArg: any, globStringObjectArg: Record<string, string>,
tsOptionsArg: CompilerOptions = {}, tsOptionsArg: CompilerOptions = {},
cwdArg: string = process.cwd(), cwdArg: string = process.cwd(),
argvArg?: any argvArg?: any
) => { ) => {
let compiledFiles: plugins.smartfile.SmartFile[] = []; let compiledFiles: any[] = [];
// Log the compilation tasks in a nice format
console.log('\n👷 TypeScript Compilation Tasks:');
Object.entries(globStringObjectArg).forEach(([source, dest]) => {
console.log(` 📂 ${source}${dest}`);
});
console.log('');
for (const keyArg in globStringObjectArg) { for (const keyArg in globStringObjectArg) {
if (globStringObjectArg[keyArg]) { // Type safety check for key
console.log( if (keyArg && typeof keyArg === 'string' && globStringObjectArg[keyArg]) {
`TypeScript assignment: transpile from ${keyArg} to ${globStringObjectArg[keyArg]}`
); // Get files matching the glob pattern
const fileTreeArray = await plugins.smartfile.fs.listFileTree(cwdArg, keyArg); const fileTreeArray = await plugins.smartfile.fs.listFileTree(cwdArg, keyArg);
const absoluteFilePathArray: string[] = plugins.smartpath.transform.toAbsolute(
fileTreeArray, // Ensure fileTreeArray contains only strings before transforming
const stringFileTreeArray = Array.isArray(fileTreeArray)
? fileTreeArray.filter((item): item is string => typeof item === 'string')
: [];
// Transform to absolute paths
const absoluteFilePathArray = plugins.smartpath.transform.toAbsolute(
stringFileTreeArray,
cwdArg cwdArg
) as string[]; ) as string[];
// Get destination directory as absolute path
const destDir: string = plugins.smartpath.transform.toAbsolute( const destDir: string = plugins.smartpath.transform.toAbsolute(
globStringObjectArg[keyArg], globStringObjectArg[keyArg],
cwdArg cwdArg
) as string; ) as string;
tsOptionsArg = {
// Update compiler options with the output directory
const updatedTsOptions: CompilerOptions = {
...tsOptionsArg, ...tsOptionsArg,
outDir: destDir, outDir: destDir,
}; };
compiledFiles = compiledFiles.concat(
compiledFiles, // Compile the files and correctly concat the results
await compileFileArray(absoluteFilePathArray, tsOptionsArg, argvArg) // Fixed: removed duplicating compiledFiles in the concat operation
); const newlyCompiledFiles = await compileFileArray(absoluteFilePathArray, updatedTsOptions, argvArg);
compiledFiles = compiledFiles.concat(newlyCompiledFiles);
} }
} }
return compiledFiles; return compiledFiles;
}; };