From 174a2ae80b3ce0db9deb59ed9cbb8daee84e6bf8 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Tue, 18 Jun 2024 11:17:43 -0500 Subject: [PATCH] feat(developer): linter API - add a Linter class that gets passed the compiled KMXPlus file, and the source file --- .../src/kmc-ldml/src/compiler/compiler.ts | 42 +++++++++++++++++-- .../kmc-ldml/src/compiler/linter-keycaps.ts | 8 ++++ developer/src/kmc-ldml/src/compiler/linter.ts | 23 ++++++++++ 3 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 developer/src/kmc-ldml/src/compiler/linter-keycaps.ts create mode 100644 developer/src/kmc-ldml/src/compiler/linter.ts diff --git a/developer/src/kmc-ldml/src/compiler/compiler.ts b/developer/src/kmc-ldml/src/compiler/compiler.ts index b972baf750..0b29cb3a66 100644 --- a/developer/src/kmc-ldml/src/compiler/compiler.ts +++ b/developer/src/kmc-ldml/src/compiler/compiler.ts @@ -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 { + // 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 { - 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; } /** diff --git a/developer/src/kmc-ldml/src/compiler/linter-keycaps.ts b/developer/src/kmc-ldml/src/compiler/linter-keycaps.ts new file mode 100644 index 0000000000..98d5f04a59 --- /dev/null +++ b/developer/src/kmc-ldml/src/compiler/linter-keycaps.ts @@ -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 { + return true; + } +} diff --git a/developer/src/kmc-ldml/src/compiler/linter.ts b/developer/src/kmc-ldml/src/compiler/linter.ts new file mode 100644 index 0000000000..2a74dfd0b5 --- /dev/null +++ b/developer/src/kmc-ldml/src/compiler/linter.ts @@ -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; +}