328 lines
12 KiB
TypeScript
328 lines
12 KiB
TypeScript
import * as plugins from './plugins.js';
|
|
import * as paths from './paths.js';
|
|
import * as tsbuild from './tsbuild.exports.js';
|
|
|
|
export const runCli = async () => {
|
|
const tsbuildCli = new plugins.smartcli.Smartcli();
|
|
|
|
/**
|
|
* the standard task compiles anything in ts/ directory to dist directory
|
|
*/
|
|
tsbuildCli.standardCommand().subscribe(async (argvArg) => {
|
|
tsbuild.compileGlobStringObject(
|
|
{
|
|
'./ts/**/*.ts': './dist_ts',
|
|
},
|
|
{},
|
|
process.cwd(),
|
|
argvArg
|
|
);
|
|
});
|
|
|
|
/**
|
|
* the custom command compiles any customDir to dist_customDir
|
|
*/
|
|
tsbuildCli.addCommand('custom').subscribe(async (argvArg) => {
|
|
const listedDirectories = argvArg._;
|
|
listedDirectories.shift(); // removes the first element that is "custom"
|
|
const compilationCommandObject: { [key: string]: string } = {};
|
|
for (const directory of listedDirectories) {
|
|
compilationCommandObject[`./${directory}/**/*.ts`] = `./dist_${directory}`;
|
|
}
|
|
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
|
|
*/
|
|
tsbuildCli.addCommand('tsfolders').subscribe(async (argvArg) => {
|
|
const tsFolders = await plugins.smartfile.fs.listFolders(paths.cwd, /^ts/);
|
|
|
|
// Now tsFolders contains all other folders except 'ts_shared' and 'ts_interfaces'
|
|
|
|
// We've established a base order. Now let's look at tspublish.json based ranking.
|
|
const tsPublishInstance = new plugins.tspublish.TsPublish();
|
|
const tsPublishModules = await tsPublishInstance.getModuleSubDirs(paths.cwd);
|
|
// tsPublishModules is an object: { [folderName]: tspublishJsonData }
|
|
|
|
// Create an array with folder names and their ranks
|
|
const foldersWithOrder = [];
|
|
|
|
for (const folder of tsFolders) {
|
|
let rank = Infinity; // Default rank if not specified
|
|
if (tsPublishModules[folder] && tsPublishModules[folder].order !== undefined) {
|
|
rank = tsPublishModules[folder].order;
|
|
}
|
|
foldersWithOrder.push({ folder, rank });
|
|
}
|
|
|
|
// Sort the folders based on rank
|
|
foldersWithOrder.sort((a, b) => a.rank - b.rank);
|
|
|
|
// Construct the sorted list of folders
|
|
const sortedTsFolders = [];
|
|
|
|
// Add the rest of the folders in sorted order
|
|
for (const item of foldersWithOrder) {
|
|
sortedTsFolders.push(item.folder);
|
|
}
|
|
|
|
// Let's make sure 'ts_shared' is always transpiled first
|
|
const ensurePosition = (folderNameArg: string, ensuredPosition: number) => {
|
|
if (tsFolders.indexOf(folderNameArg) > -1 && Object.keys(tsPublishModules).indexOf(folderNameArg) === -1) {
|
|
sortedTsFolders.splice(tsFolders.indexOf(folderNameArg), 1);
|
|
sortedTsFolders.splice(ensuredPosition, 0, folderNameArg);
|
|
}
|
|
}
|
|
|
|
ensurePosition('ts_interfaces', 0);
|
|
ensurePosition('ts_shared', 1);
|
|
|
|
|
|
const compilationCommandObject: { [key: string]: string } = {};
|
|
const folderCount = sortedTsFolders.length;
|
|
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) {
|
|
compilationCommandObject[`./${tsFolder}/**/*.ts`] = `./dist_${tsFolder}`;
|
|
}
|
|
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();
|
|
};
|