feat(developer): linter API

- add a Linter class that gets passed the compiled KMXPlus file, and the source file
This commit is contained in:
Steven R. Loomis 2024-06-18 11:17:43 -05:00
parent 8deae5137c
commit 174a2ae80b
3 changed files with 70 additions and 3 deletions

View file

@ -18,6 +18,7 @@ import { SectionIdent, constants } from '@keymanapp/ldml-keyboard-constants';
import { KmnCompiler } from '@keymanapp/kmc-kmn';
import { KMXPlusMetadataCompiler } from './metadata-compiler.js';
import { LdmlKeyboardVisualKeyboardCompiler } from './visual-keyboard-compiler.js';
import { LinterKeycaps } from './linter-keycaps.js';
//KMW17.0: import { LdmlKeyboardKeymanWebCompiler } from './keymanweb-compiler.js';
export const SECTION_COMPILERS = [
@ -41,6 +42,11 @@ export const SECTION_COMPILERS = [
TranCompiler,
];
/** list of linters, in order. */
const LINTERS = [
LinterKeycaps,
];
/**
* @public
* Internal in-memory build artifacts from a successful compilation
@ -279,16 +285,46 @@ export class LdmlKeyboardCompiler implements KeymanCompiler {
return source;
}
/** Materialize the linters against the built datafile */
private buildLinters(source: LDMLKeyboardXMLSourceFile, kmx: KMXPlus.KMXPlusFile) {
return LINTERS.map(c => new c(source, kmx, this.callbacks));
}
/**
* Runs any linter steps
* @internal
*/
private async lint(source: LDMLKeyboardXMLSourceFile, kmx: KMXPlus.KMXPlusFile) : Promise<boolean> {
// run each of the linters
for (const linter of this.buildLinters(source, kmx)) {
if (!await linter.lint()) {
return false;
}
}
return true;
}
/**
* @internal
* Validates that the LDML keyboard source file and lints. Actually just
* compiles the keyboard and returns `true` if everything is good...
* Validates that the LDML keyboard source file and lints.
* @param source - in-memory representation of LDML keyboard xml file
* @returns true if the file validates
*/
public async validate(source: LDMLKeyboardXMLSourceFile): Promise<boolean> {
return !!(await this.compile(source, true));
// We need to compile in order to validate.
const kmx = await this.compile(source, true);
if (!kmx) {
return false;
}
// Run the linters
if (!await this.lint(source, kmx)) {
return false;
}
// We are valid if we have a keyboard file at this point.
return !!kmx;
}
/**

View file

@ -0,0 +1,8 @@
import { Linter } from "./linter.js";
/** A linter concerned with the display of keycaps */
export class LinterKeycaps extends Linter {
public async lint(): Promise<boolean> {
return true;
}
}

View file

@ -0,0 +1,23 @@
import { LDMLKeyboard, KMXPlus, CompilerCallbacks } from "@keymanapp/common-types";
/** newable interface to Linter c'tor */
export type LinterNew = new (source: LDMLKeyboard.LDMLKeyboardXMLSourceFile, kmx: KMXPlus.KMXPlusFile, callbacks: CompilerCallbacks) => Linter;
/** Abstract interface for a class which provides additional hints against a compiled keyboard file */
export abstract class Linter {
protected readonly keyboard3: LDMLKeyboard.LKKeyboard;
protected readonly callbacks: CompilerCallbacks;
protected readonly kmx: KMXPlus.KMXPlusFile;
constructor(source: LDMLKeyboard.LDMLKeyboardXMLSourceFile, kmx: KMXPlus.KMXPlusFile, callbacks: CompilerCallbacks) {
this.keyboard3 = source.keyboard3;
this.callbacks = callbacks;
this.kmx = kmx;
}
/**
* Check the file for any additional needed hints.
* Add the hints to the callbacks.
* @returns false on catastrophic failure of the linter.
*/
public abstract lint() : Promise<boolean>;
}