mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-21 23:27:39 +00:00
feat(developer): add --log-format to kmc
Adds logFormat of tsv vs formatted for kmc, in order to allow for "porcelain" style log formatting when we interop with TIKE.
This commit is contained in:
parent
0d96437cb8
commit
d2f4e5f2c7
7 changed files with 75 additions and 23 deletions
|
|
@ -25,7 +25,9 @@ export { defaultCompilerOptions, CompilerBaseOptions, CompilerCallbacks, Compile
|
|||
CompilerErrorSeverity, CompilerPathCallbacks, CompilerFileSystemCallbacks, CompilerCallbackOptions,
|
||||
CompilerError, CompilerMessageSpec, compilerErrorSeverity, CompilerErrorMask, CompilerFileCallbacks, compilerErrorSeverityName,
|
||||
compilerExceptionToString, compilerErrorFormatCode,
|
||||
compilerLogLevelToSeverity, CompilerLogLevel, compilerEventFormat, ALL_COMPILER_LOG_LEVELS } from './util/compiler-interfaces.js';
|
||||
compilerLogLevelToSeverity, CompilerLogLevel, compilerEventFormat, ALL_COMPILER_LOG_LEVELS,
|
||||
ALL_COMPILER_LOG_FORMATS, CompilerLogFormat,
|
||||
} from './util/compiler-interfaces.js';
|
||||
export { CommonTypesMessages } from './util/common-events.js';
|
||||
|
||||
export * as TouchLayout from './keyman-touch-layout/keyman-touch-layout-file.js';
|
||||
|
|
|
|||
|
|
@ -74,11 +74,20 @@ export class CompilerError {
|
|||
* @param filename
|
||||
* @returns
|
||||
*/
|
||||
static formatFilename(filename: string): string {
|
||||
static formatFilename(filename: string, options?: {
|
||||
fullPath?: boolean,
|
||||
forwardSlashes?: boolean
|
||||
}): string {
|
||||
if(!filename) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if(options?.fullPath) {
|
||||
return options?.forwardSlashes ?
|
||||
filename.replaceAll(/\\/g, '/') :
|
||||
filename.replaceAll(/\//g, '\\');
|
||||
}
|
||||
|
||||
let x = filename.lastIndexOf('/');
|
||||
if(x < 0) {
|
||||
x = filename.lastIndexOf('\\');
|
||||
|
|
@ -239,6 +248,7 @@ export interface CompilerFileSystemCallbacks {
|
|||
|
||||
export interface CompilerCallbackOptions {
|
||||
logLevel?: CompilerLogLevel;
|
||||
logFormat?: CompilerLogFormat;
|
||||
color?: boolean; // null or undefined == use console default
|
||||
compilerWarningsAsErrors?: boolean;
|
||||
};
|
||||
|
|
@ -355,6 +365,10 @@ export interface CompilerBaseOptions {
|
|||
* all messages are still reported to the internal log)
|
||||
*/
|
||||
logLevel?: CompilerLogLevel;
|
||||
/**
|
||||
* Format of output for log to console
|
||||
*/
|
||||
logFormat?: CompilerLogFormat;
|
||||
/**
|
||||
* Optional output file for activities that generate output
|
||||
*/
|
||||
|
|
@ -390,6 +404,7 @@ export interface CompilerOptions extends CompilerBaseOptions {
|
|||
|
||||
export const defaultCompilerOptions: CompilerOptions = {
|
||||
logLevel: 'info',
|
||||
logFormat: 'formatted',
|
||||
// outFile: (undefined)
|
||||
saveDebug: false,
|
||||
shouldAddCompilerVersion: true,
|
||||
|
|
@ -443,3 +458,11 @@ export const compilerLogLevelToSeverity: {[index in CompilerLogLevel]: number} =
|
|||
'info': CompilerErrorSeverity.Info,
|
||||
'debug': CompilerErrorSeverity.Info
|
||||
};
|
||||
|
||||
export const ALL_COMPILER_LOG_FORMATS = [
|
||||
'tsv',
|
||||
'formatted'
|
||||
] as const;
|
||||
|
||||
type CompilerLogFormatTuple = typeof ALL_COMPILER_LOG_FORMATS;
|
||||
export type CompilerLogFormat = CompilerLogFormatTuple[number];
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ function commandOptionsToCompilerOptions(options: any): CompilerOptions {
|
|||
// CompilerBaseOptions
|
||||
outFile: options.outFile,
|
||||
logLevel: options.logLevel,
|
||||
logFormat: options.logFormat,
|
||||
color: options.color,
|
||||
// CompilerOptions
|
||||
shouldAddCompilerVersion: options.compilerVersion,
|
||||
|
|
@ -112,22 +113,22 @@ async function build(filename: string, parentCallbacks: NodeCompilerCallbacks, o
|
|||
if(fs.statSync(filename).isDirectory()) {
|
||||
buildFilename = path.join(buildFilename, path.basename(buildFilename) + KeymanFileTypes.Source.Project);
|
||||
}
|
||||
buildFilename = path.relative(process.cwd(), buildFilename).replace(/\\/g, '/');
|
||||
const relativeFilename = path.relative(process.cwd(), buildFilename).replace(/\\/g, '/');
|
||||
|
||||
const callbacks = new CompilerFileCallbacks(buildFilename, options, parentCallbacks);
|
||||
callbacks.reportMessage(InfrastructureMessages.Info_BuildingFile({filename:buildFilename}));
|
||||
callbacks.reportMessage(InfrastructureMessages.Info_BuildingFile({filename:buildFilename, relativeFilename}));
|
||||
|
||||
let result = await builder.build(filename, callbacks, options);
|
||||
result = result && !callbacks.hasFailureMessage();
|
||||
if(result) {
|
||||
callbacks.reportMessage(builder instanceof BuildProject
|
||||
? InfrastructureMessages.Info_ProjectBuiltSuccessfully({filename:buildFilename})
|
||||
: InfrastructureMessages.Info_FileBuiltSuccessfully({filename:buildFilename})
|
||||
? InfrastructureMessages.Info_ProjectBuiltSuccessfully({filename:buildFilename, relativeFilename})
|
||||
: InfrastructureMessages.Info_FileBuiltSuccessfully({filename:buildFilename, relativeFilename})
|
||||
);
|
||||
} else {
|
||||
callbacks.reportMessage(builder instanceof BuildProject
|
||||
? InfrastructureMessages.Info_ProjectNotBuiltSuccessfully({filename:buildFilename})
|
||||
: InfrastructureMessages.Info_FileNotBuiltSuccessfully({filename:buildFilename})
|
||||
? InfrastructureMessages.Info_ProjectNotBuiltSuccessfully({filename:buildFilename, relativeFilename})
|
||||
: InfrastructureMessages.Info_FileNotBuiltSuccessfully({filename:buildFilename, relativeFilename})
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ class ProjectBuilder {
|
|||
|
||||
const buildFilename = path.relative(process.cwd(), infile).replace(/\\/g, '/');
|
||||
const callbacks = new CompilerFileCallbacks(buildFilename, options, this.callbacks);
|
||||
callbacks.reportMessage(InfrastructureMessages.Info_BuildingFile({filename: buildFilename}));
|
||||
callbacks.reportMessage(InfrastructureMessages.Info_BuildingFile({filename: infile, relativeFilename:buildFilename}));
|
||||
|
||||
fs.mkdirSync(path.dirname(options.outFile), {recursive:true});
|
||||
|
||||
|
|
@ -98,9 +98,9 @@ class ProjectBuilder {
|
|||
result = result && !callbacks.hasFailureMessage(this.options.compilerWarningsAsErrors ?? this.project.options.compilerWarningsAsErrors);
|
||||
|
||||
if(result) {
|
||||
callbacks.reportMessage(InfrastructureMessages.Info_FileBuiltSuccessfully({filename: buildFilename}));
|
||||
callbacks.reportMessage(InfrastructureMessages.Info_FileBuiltSuccessfully({filename: infile, relativeFilename:buildFilename}));
|
||||
} else {
|
||||
callbacks.reportMessage(InfrastructureMessages.Info_FileNotBuiltSuccessfully({filename: buildFilename}));
|
||||
callbacks.reportMessage(InfrastructureMessages.Info_FileNotBuiltSuccessfully({filename: infile, relativeFilename: buildFilename}));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ export class InfrastructureMessages {
|
|||
static FATAL_UnexpectedException = SevFatal | 0x0001;
|
||||
|
||||
// For this message, we override the filename with the passed-in file. A bit of a hack but does the job
|
||||
static Info_BuildingFile = (o:{filename:string}) => ({filename:o.filename, ...m(this.INFO_BuildingFile,
|
||||
`Building ${o.filename}`)});
|
||||
static Info_BuildingFile = (o:{filename:string,relativeFilename:string}) => ({filename:o.filename, ...m(this.INFO_BuildingFile,
|
||||
`Building ${o.relativeFilename}`)});
|
||||
static INFO_BuildingFile = SevInfo | 0x0002;
|
||||
|
||||
static Error_FileDoesNotExist = (o:{filename:string}) => m(this.ERROR_FileDoesNotExist,
|
||||
|
|
@ -29,13 +29,13 @@ export class InfrastructureMessages {
|
|||
static ERROR_OutFileNotValidForProjects = SevError | 0x0005;
|
||||
|
||||
// For this message, we override the filename with the passed-in file. A bit of a hack but does the job
|
||||
static Info_FileBuiltSuccessfully = (o:{filename:string}) => ({filename:o.filename, ...m(this.INFO_FileBuiltSuccessfully,
|
||||
`${o.filename} built successfully.`)});
|
||||
static Info_FileBuiltSuccessfully = (o:{filename:string,relativeFilename:string}) => ({filename:o.filename, ...m(this.INFO_FileBuiltSuccessfully,
|
||||
`${o.relativeFilename} built successfully.`)});
|
||||
static INFO_FileBuiltSuccessfully = SevInfo | 0x0006;
|
||||
|
||||
// For this message, we override the filename with the passed-in file. A bit of a hack but does the job
|
||||
static Info_FileNotBuiltSuccessfully = (o:{filename:string}) => ({filename:o.filename, ...m(this.INFO_FileNotBuiltSuccessfully,
|
||||
`${o.filename} failed to build.`)});
|
||||
static Info_FileNotBuiltSuccessfully = (o:{filename:string,relativeFilename:string}) => ({filename:o.filename, ...m(this.INFO_FileNotBuiltSuccessfully,
|
||||
`${o.relativeFilename} failed to build.`)});
|
||||
static INFO_FileNotBuiltSuccessfully = SevInfo | 0x0007;
|
||||
|
||||
static Error_InvalidProjectFile = (o:{message:string}) => m(this.ERROR_InvalidProjectFile,
|
||||
|
|
@ -51,13 +51,13 @@ export class InfrastructureMessages {
|
|||
static ERROR_UnknownFileFormat = SevError | 0x000A;
|
||||
|
||||
// For this message, we override the filename with the passed-in file. A bit of a hack but does the job
|
||||
static Info_ProjectBuiltSuccessfully = (o:{filename:string}) => ({filename:o.filename, ...m(this.INFO_ProjectBuiltSuccessfully,
|
||||
`Project ${o.filename} built successfully.`)});
|
||||
static Info_ProjectBuiltSuccessfully = (o:{filename:string,relativeFilename:string}) => ({filename:o.filename, ...m(this.INFO_ProjectBuiltSuccessfully,
|
||||
`Project ${o.relativeFilename} built successfully.`)});
|
||||
static INFO_ProjectBuiltSuccessfully = SevInfo | 0x000B;
|
||||
|
||||
// For this message, we override the filename with the passed-in file. A bit of a hack but does the job
|
||||
static Info_ProjectNotBuiltSuccessfully = (o:{filename:string}) => ({filename:o.filename, ...m(this.INFO_ProjectNotBuiltSuccessfully,
|
||||
`Project ${o.filename} failed to build.`)});
|
||||
static Info_ProjectNotBuiltSuccessfully = (o:{filename:string,relativeFilename:string}) => ({filename:o.filename, ...m(this.INFO_ProjectNotBuiltSuccessfully,
|
||||
`Project ${o.relativeFilename} failed to build.`)});
|
||||
static INFO_ProjectNotBuiltSuccessfully = SevInfo | 0x000C;
|
||||
|
||||
static Info_TooManyMessages = (o:{count:number}) => m(this.INFO_TooManyMessages,
|
||||
|
|
|
|||
|
|
@ -154,6 +154,28 @@ export class NodeCompilerCallbacks implements CompilerCallbacks {
|
|||
event.filename = this.messageFilename;
|
||||
}
|
||||
|
||||
this.printMessage(event);
|
||||
}
|
||||
|
||||
private printMessage(event: CompilerEvent) {
|
||||
if(this.options.logFormat == 'tsv') {
|
||||
this.printTsvMessage(event);
|
||||
} else {
|
||||
this.printFormattedMessage(event);
|
||||
}
|
||||
}
|
||||
|
||||
private printTsvMessage(event: CompilerEvent) {
|
||||
process.stdout.write([
|
||||
CompilerError.formatFilename(event.filename, {fullPath:true, forwardSlashes:false}),
|
||||
CompilerError.formatLine(event.line),
|
||||
CompilerError.formatSeverity(event.code),
|
||||
CompilerError.formatCode(event.code),
|
||||
CompilerError.formatMessage(event.message)
|
||||
].join('\t') + '\n');
|
||||
}
|
||||
|
||||
private printFormattedMessage(event: CompilerEvent) {
|
||||
const severityColor = severityColors[CompilerError.severity(event.code)] ?? color.reset;
|
||||
const messageColor = this.messageSpecialColor(event) ?? color.reset;
|
||||
process.stdout.write(
|
||||
|
|
@ -172,7 +194,6 @@ export class NodeCompilerCallbacks implements CompilerCallbacks {
|
|||
// Special case: we'll add a blank line after project builds
|
||||
process.stdout.write('\n');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { ALL_COMPILER_LOG_LEVELS } from "@keymanapp/common-types";
|
||||
import { ALL_COMPILER_LOG_FORMATS, ALL_COMPILER_LOG_LEVELS } from "@keymanapp/common-types";
|
||||
import { Command, Option } from "commander";
|
||||
import KEYMAN_VERSION from "@keymanapp/keyman-version";
|
||||
|
||||
|
|
@ -23,6 +23,10 @@ export class BaseOptions {
|
|||
return program.addOption(new Option('-l, --log-level <logLevel>', 'Log level').choices(ALL_COMPILER_LOG_LEVELS).default('info'));
|
||||
}
|
||||
|
||||
public static addLogFormat(program: Command) {
|
||||
return program.addOption(new Option('-l, --log-format <logFormat>', 'Log format').choices(ALL_COMPILER_LOG_FORMATS).default('formatted'));
|
||||
}
|
||||
|
||||
public static addOutFile(program: Command) {
|
||||
return program.option('-o, --out-file <filename>', 'Override the default path and filename for the output file')
|
||||
}
|
||||
|
|
@ -31,6 +35,7 @@ export class BaseOptions {
|
|||
return [
|
||||
this.addVersion,
|
||||
this.addLogLevel,
|
||||
this.addLogFormat,
|
||||
this.addOutFile,
|
||||
].reduce((p,f) => f(p), program);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue