@ibyar/cli
Version:
The Ibyar/Aurora CLI tool
179 lines • 7.47 kB
JavaScript
import '../directives/register.js';
import ts from 'typescript/lib/tsserverlibrary.js';
import { afterDeclarationsCompileComponentOptions } from '../transformer/after-declarations-component.js';
import { afterDeclarationsCompileDirectiveOptions } from '../transformer/after-declarations-directive.js';
import { beforeCompileComponentOptions } from '../transformer/before-component.js';
import { beforeCompileDirectiveOptions } from '../transformer/before-directive.js';
import { scanDirectivesTypeVisitor } from '../transformer/scan-directives.js';
import { dirname, join, resolve, relative } from 'path';
import { existsSync } from 'fs';
export function getTransformers(program) {
return {
before: [
beforeCompileDirectiveOptions(program),
beforeCompileComponentOptions(program),
],
after: [],
afterDeclarations: [
afterDeclarationsCompileComponentOptions(program),
afterDeclarationsCompileDirectiveOptions(program),
],
};
}
export function scanDirectives(program) {
program.getSourceFiles().forEach(scanDirectivesTypeVisitor);
}
export function scanDirectivesOnceAsTransformer() {
let scanDone = false;
let ignoreTransformer = (context) => sourceFile => sourceFile;
return (program) => {
if (scanDone) {
return ignoreTransformer;
}
scanDone = true;
scanDirectives(program);
return ignoreTransformer;
};
}
export function emitProgram(program) {
scanDirectives(program);
program.emit(undefined, undefined, undefined, undefined, getTransformers(program));
}
export function compileFiles(files, options) {
const host = ts.createCompilerHost(options);
const program = ts.createProgram(files, options, host);
emitProgram(program);
}
const formatHost = {
getCanonicalFileName: path => path,
getCurrentDirectory: ts.sys.getCurrentDirectory,
getNewLine: () => ts.sys.newLine
};
function reportDiagnostic(diagnostic) {
console.error("Error", diagnostic.code, ":", ts.flattenDiagnosticMessageText(diagnostic.messageText, formatHost.getNewLine()));
}
/**
* Prints a diagnostic every time the watch status changes.
* This is mainly for messages like "Starting compilation" or "Compilation completed".
*/
function reportWatchStatusChanged(diagnostic) {
console.info(ts.formatDiagnostic(diagnostic, formatHost));
}
export function compileAndWatchFiles(configPath, cmd) {
const createProgram = ts.createSemanticDiagnosticsBuilderProgram;
const host = ts.createWatchCompilerHost(configPath, cmd.options, ts.sys, createProgram, reportDiagnostic, reportWatchStatusChanged, cmd.watchOptions);
const programFactory = ts.createWatchProgram(host);
const programBuilder = programFactory.getProgram();
const program = programBuilder.getProgram();
emitProgram(program);
}
export function patchProgram(tsProgram) {
const program = tsProgram;
program.__emit = program.emit;
const programTransformers = getTransformers(program);
program.emit = (targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers) => {
if (!customTransformers) {
return program.__emit(targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, programTransformers);
}
return program.__emit(targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, {
before: [...(customTransformers?.before ?? []), ...programTransformers.before],
after: [...(customTransformers?.after ?? []), ...programTransformers.after],
afterDeclarations: [...(customTransformers?.afterDeclarations ?? []), ...programTransformers.afterDeclarations],
});
};
}
export function createEmitAndSemanticDiagnosticsBuilderProgram(rootNames, options, host, oldProgram, configFileParsingDiagnostics, projectReferences) {
const program = ts.createEmitAndSemanticDiagnosticsBuilderProgram(rootNames, options, host, oldProgram, configFileParsingDiagnostics, projectReferences);
scanDirectives(program.getProgram());
patchProgram(program.getProgram());
return program;
}
const diagHost = {
getCanonicalFileName: function (fileName) {
return ts.sys.resolvePath(fileName);
},
getCurrentDirectory: function () {
return ts.sys.getCurrentDirectory();
},
getNewLine: function () {
return ts.sys.newLine;
}
};
function getTime() {
const date = new Date();
return `[${date.toLocaleTimeString('en-US')}]`;
}
function reportDiag(diag) {
let output = ts.sys.writeOutputIsTTY?.() ?
ts.formatDiagnosticsWithColorAndContext([diag], diagHost) :
ts.formatDiagnostic(diag, diagHost);
output = output.replace(/^[\r\n]+/, '').replace(/[\r\n]+$/, '');
ts.sys.write(`${getTime()} ${output}` + '\n');
}
function reportError(errorCount, filesInError) {
ts.sys.write(`Error count: ${errorCount}\n`);
filesInError.forEach(error => ts.sys.write(`Error file: ${error?.fileName}(${error?.line})\n`));
}
function printProjects(solution) {
const projects = solution?.getBuildOrder();
if (Array.isArray(projects) && projects?.length) {
console.log(`${getTime()} Projects in this build: `);
projects.forEach(project => console.log(`\t* ${relative(process.cwd(), project)}`));
}
}
export function compileSolution(configFilePath, cmd) {
const rootNames = [dirname(configFilePath)];
const solutionHost = ts.createSolutionBuilderHost(ts.sys, createEmitAndSemanticDiagnosticsBuilderProgram, reportDiag, reportDiag, reportError);
const solution = ts.createSolutionBuilder(solutionHost, rootNames, {});
printProjects(solution);
solution.build();
}
export function compileSolutionAndWatch(configFilePath, cmd) {
const rootNames = [dirname(configFilePath)];
const solutionHost = ts.createSolutionBuilderWithWatchHost(ts.sys, createEmitAndSemanticDiagnosticsBuilderProgram, reportDiag, reportDiag, reportDiag);
const solution = ts.createSolutionBuilderWithWatch(solutionHost, rootNames, {}, cmd.watchOptions);
printProjects(solution);
solution.build();
}
export function getConfigPath() {
const args = process.argv.slice(2);
let tsconfig = args.filter(arg => arg.includes('tsconfig'))[0];
if (!tsconfig && args.length > 1 /** -b */) {
const path = join(args.at(-1), 'tsconfig.json');
if (existsSync(path)) {
tsconfig = resolve(path);
}
}
const configPath = ts.findConfigFile('./', /*searchPath*/ ts.sys.fileExists, tsconfig ?? 'tsconfig.json');
if (!configPath) {
throw new Error("Could not find a valid 'tsconfig.json'.");
}
return configPath;
}
export function compileArgs() {
const configPath = getConfigPath();
const cmd = ts.getParsedCommandLineOfConfigFile(configPath, {}, ts.sys);
if (!cmd) {
return;
}
if (cmd.projectReferences?.length) {
compileSolution(configPath, cmd);
}
else {
compileFiles(cmd.fileNames, cmd.options);
}
}
export function compileAndWatchArgs() {
const configPath = getConfigPath();
const cmd = ts.getParsedCommandLineOfConfigFile(configPath, {}, ts.sys);
if (!cmd) {
return;
}
if (cmd.projectReferences?.length) {
compileSolutionAndWatch(configPath, cmd);
}
else {
compileAndWatchFiles(configPath, cmd);
}
}
//# sourceMappingURL=compiler.js.map