// 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 => { 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(); const program = plugins.typescript.createProgram(fileNames, options); // Check for pre-emit diagnostics first const preEmitDiagnostics = plugins.typescript.getPreEmitDiagnostics(program); let hasErrors = false; // Log pre-emit diagnostics if any if (preEmitDiagnostics.length > 0) { preEmitDiagnostics.forEach((diagnostic) => { hasErrors = true; 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')}` ); } }); } // Only continue to emit phase if no pre-emit errors if (hasErrors) { console.error('TypeScript pre-emit checks failed. Please fix the issues above.'); process.exit(1); } // If no pre-emit errors, proceed with emit const emitResult = program.emit(); // Check for emit diagnostics if (emitResult.diagnostics.length > 0) { emitResult.diagnostics.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; }; /** * Function to check if a TypeScript file can be emitted without actually emitting it */ export const emitCheck = async ( fileNames: string[], options: plugins.typescript.CompilerOptions = {}, argvArg?: any ): Promise => { console.log(`Checking if ${fileNames.length} files can be emitted...`); // Create a program const program = plugins.typescript.createProgram(fileNames, { ...options, noEmit: true }); // Check for pre-emit diagnostics const preEmitDiagnostics = plugins.typescript.getPreEmitDiagnostics(program); let hasErrors = false; // Log pre-emit diagnostics if any if (preEmitDiagnostics.length > 0) { preEmitDiagnostics.forEach((diagnostic) => { hasErrors = true; 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')}` ); } }); } // Run the emit phase but with noEmit: true to check for emit errors without producing files const emitResult = program.emit(undefined, undefined, undefined, true); // Check for emit diagnostics if (emitResult.diagnostics.length > 0) { emitResult.diagnostics.forEach((diagnostic) => { hasErrors = true; 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')}` ); } }); } if (!hasErrors && !emitResult.emitSkipped) { console.log('TypeScript emit check passed! File can be emitted successfully.'); return true; } else { console.error('TypeScript emit check failed. Please fix the issues above.'); return false; } };