diff --git a/common/web/types/src/kpj/kpj-file-reader.ts b/common/web/types/src/kpj/kpj-file-reader.ts
index 21e92c975b..9ac1bc8dbc 100644
--- a/common/web/types/src/kpj/kpj-file-reader.ts
+++ b/common/web/types/src/kpj/kpj-file-reader.ts
@@ -23,6 +23,12 @@ export class KPJFileReader {
});
parser.parseString(file, (e: unknown, r: unknown) => { data = r as KPJFile });
+ for(let file of data.KeymanDeveloperProject?.Files?.File) {
+ // xml2js imports as '' so we will just delete the empty string
+ if(typeof file.Details == 'string') {
+ delete file.Details;
+ }
+ }
data = this.boxArrays(data);
return data as KPJFile;
}
diff --git a/developer/src/kmc-kmn/src/kmw-compiler/kmw-compiler.ts b/developer/src/kmc-kmn/src/kmw-compiler/kmw-compiler.ts
index a4521b4fdc..65e2dbd13c 100644
--- a/developer/src/kmc-kmn/src/kmw-compiler/kmw-compiler.ts
+++ b/developer/src/kmc-kmn/src/kmw-compiler/kmw-compiler.ts
@@ -8,7 +8,7 @@ import { VisualKeyboardFromFile } from "./visual-keyboard-compiler.js";
import { CompilerResult, STORETYPE_DEBUG, STORETYPE_OPTION, STORETYPE_RESERVED } from "../compiler/compiler.js";
function requote(s: string): string {
- return "'" + s.replaceAll(/(['\\])/, "\\$1") + "'";
+ return "'" + s.replaceAll(/(['\\])/g, "\\$1") + "'";
}
export function RequotedString(s: string, RequoteSingleQuotes: boolean = false): string {
diff --git a/developer/src/kmc-kmn/src/kmw-compiler/util.ts b/developer/src/kmc-kmn/src/kmw-compiler/util.ts
index 21aa9d3c14..1b67b9c719 100644
--- a/developer/src/kmc-kmn/src/kmw-compiler/util.ts
+++ b/developer/src/kmc-kmn/src/kmw-compiler/util.ts
@@ -142,15 +142,17 @@ export function ExpandSentinel(fk: KMX.KEYBOARD, pwsz: string, x: number): TSent
};
break;
case KMX.KMXFile.CODE_RESETOPT: // I3429
+ const resetOptIdx = pwsz.charCodeAt(x) - 1;
result.ResetOpt = {
- StoreIndex: pwsz.charCodeAt(x) - 1,
- Store: fk.stores[result.ResetOpt.StoreIndex]
+ StoreIndex: resetOptIdx,
+ Store: fk.stores[resetOptIdx]
};
break;
case KMX.KMXFile.CODE_SAVEOPT: // I3429
+ const saveOptIdx = pwsz.charCodeAt(x) - 1;
result.SaveOpt = {
- StoreIndex: pwsz.charCodeAt(x) - 1,
- Store: fk.stores[result.SaveOpt.StoreIndex]
+ StoreIndex: saveOptIdx,
+ Store: fk.stores[saveOptIdx]
};
break;
case KMX.KMXFile.CODE_IFOPT: // I3429
diff --git a/developer/src/kmc-kmn/src/kmw-compiler/validate-layout-file.ts b/developer/src/kmc-kmn/src/kmw-compiler/validate-layout-file.ts
index 1136a49c6a..bb9ebb1cbd 100644
--- a/developer/src/kmc-kmn/src/kmw-compiler/validate-layout-file.ts
+++ b/developer/src/kmc-kmn/src/kmw-compiler/validate-layout-file.ts
@@ -69,6 +69,12 @@ function CheckKey(
FRequiredKeys: TRequiredKey[],
FDictionary: string[]
): boolean { // I4119
+ //
+ // Coerce missing ID and Text to empty strings for additional tests
+ //
+
+ FId = FId ?? '';
+ FText = FText ?? '';
//
// Check that each touch layer has K_LOPT, [K_ROPT,] K_BKSP, K_ENTER
@@ -101,7 +107,7 @@ function CheckKey(
//
if(FId.trim() == '') {
- if(!(FKeyType in [TouchLayout.TouchLayoutKeySp.blank, TouchLayout.TouchLayoutKeySp.spacer]) && FNextLayer == '') {
+ if(!([TouchLayout.TouchLayoutKeySp.blank, TouchLayout.TouchLayoutKeySp.spacer].includes(FKeyType)) && FNextLayer == '') {
callbacks.reportMessage(KmwCompilerMessages.Warn_TouchLayoutUnidentifiedKey({layerId: layer.id}));
}
return true;
@@ -122,7 +128,7 @@ function CheckKey(
// Check that each custom key code has at least *a* rule associated with it
//
- if (FValid == TKeyIdType.Key_Touch && FNextLayer == '' && FKeyType in [TouchLayout.TouchLayoutKeySp.normal, TouchLayout.TouchLayoutKeySp.deadkey]) {
+ if (FValid == TKeyIdType.Key_Touch && FNextLayer == '' && [TouchLayout.TouchLayoutKeySp.normal, TouchLayout.TouchLayoutKeySp.deadkey].includes(FKeyType)) {
// Search for the key in the key dictionary - ignore K_LOPT, K_ROPT...
if(FDictionary.indexOf(FId) < 0) {
callbacks.reportMessage(KmwCompilerMessages.Warn_TouchLayoutCustomKeyNotDefined({keyId: FId, platformName: platformId, layerId: layer.id}));
@@ -139,7 +145,7 @@ function CheckKey(
if((CSpecialText10.includes(FText) || CSpecialText14.includes(FText)) &&
!CSpecialText14ZWNJ.includes(FText) &&
!IsKeyboardVersion14OrLater() &&
- !(FKeyType in [TouchLayout.TouchLayoutKeySp.special, TouchLayout.TouchLayoutKeySp.specialActive])) {
+ !([TouchLayout.TouchLayoutKeySp.special, TouchLayout.TouchLayoutKeySp.specialActive].includes(FKeyType))) {
callbacks.reportMessage(KmwCompilerMessages.Warn_TouchLayoutSpecialLabelOnNormalKey({
keyId: FId,
platformName: platformId,
@@ -161,7 +167,7 @@ function CheckDictionaryKeyValidity(fk: KMX.KEYBOARD, FDictionary: string[]) {
continue;
}
- if(KeyIdType(FDictionary[i]) in [TKeyIdType.Key_Invalid, TKeyIdType.Key_Constant]) {
+ if([TKeyIdType.Key_Invalid, TKeyIdType.Key_Constant].includes(KeyIdType(FDictionary[i]))) {
for(let fgp of fk.groups) {
if(fgp.fUsingKeys) {
for(let fkp of fgp.keys) {
@@ -217,9 +223,9 @@ export function ValidateLayoutFile(fk: KMX.KEYBOARD, FDebug: boolean, sLayoutFil
// Test that the font matches on all platforms // I4872
if(FTouchLayoutFont == '') {
- FTouchLayoutFont = platform.font;
+ FTouchLayoutFont = platform.font ?? '';
}
- else if(platform.font.toLowerCase() != FTouchLayoutFont) {
+ else if(platform.font?.toLowerCase() ?? '' != FTouchLayoutFont) {
callbacks.reportMessage(KmwCompilerMessages.Warn_TouchLayoutFontShouldBeSameForAllPlatforms());
// TODO: why support multiple font values if it has to be the same across all platforms?!
}
diff --git a/developer/src/kmc-package/src/compiler/package-validation.ts b/developer/src/kmc-package/src/compiler/package-validation.ts
index 07fb5baaec..b663545fb0 100644
--- a/developer/src/kmc-package/src/compiler/package-validation.ts
+++ b/developer/src/kmc-package/src/compiler/package-validation.ts
@@ -13,8 +13,8 @@ const MODEL_ID_PATTERN_PACKAGE = /^[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_-]*\.[a-z_][a
// const MODEL_ID_PATTERN_PROJECT = /^[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_-]*\.[a-z_][a-z0-9_]*\.model\.kpj$/;
// "Content files" within the package should adhere to these pattern:
-const CONTENT_FILE_BASENAME_PATTERN = /^[a-z0-9_+.-]+$/;
-const CONTENT_FILE_EXTENSION_PATTERN = /^\.[a-z0-9_]+$/;
+const CONTENT_FILE_BASENAME_PATTERN = /^[a-z0-9_+.-]+$/i; // base names can be case insensitive
+const CONTENT_FILE_EXTENSION_PATTERN = /^\.[a-z0-9_]+$/; // extensions should be lower-case
export class PackageValidation {
diff --git a/developer/src/kmc-package/src/compiler/package-version-validation.ts b/developer/src/kmc-package/src/compiler/package-version-validation.ts
index 47e3e39b5c..b4f099ce13 100644
--- a/developer/src/kmc-package/src/compiler/package-version-validation.ts
+++ b/developer/src/kmc-package/src/compiler/package-version-validation.ts
@@ -73,7 +73,7 @@ export class PackageVersionValidation {
if(result) {
if(followKeyboardVersion) {
- kmp.info.version.description = kmp.keyboards[0].version;
+ kmp.info.version = {description: kmp.keyboards[0].version};
}
else if(kmp.info.version?.description != kmp.keyboards[0].version) {
// Only need to compare against first keyboard as we compare keyboards above
diff --git a/developer/src/kmc/src/commands/build.ts b/developer/src/kmc/src/commands/build.ts
index c2e550ccc5..0f3fb38200 100644
--- a/developer/src/kmc/src/commands/build.ts
+++ b/developer/src/kmc/src/commands/build.ts
@@ -4,7 +4,7 @@ import { buildActivities } from './buildClasses/buildActivities.js';
import { BuildProject } from './buildClasses/BuildProject.js';
import { NodeCompilerCallbacks } from '../messages/NodeCompilerCallbacks.js';
import { InfrastructureMessages } from '../messages/messages.js';
-import { CompilerOptions, KeymanFileTypes } from '@keymanapp/common-types';
+import { CompilerErrorSeverity, CompilerOptions, KeymanFileTypes } from '@keymanapp/common-types';
import { BaseOptions } from '../util/baseOptions.js';
@@ -79,8 +79,11 @@ async function build(filename: string, options: CompilerOptions): Promise failureCodes.includes(m.code & CompilerErrorSeverity.Severity_Mask));
+ if(result && firstFailureMessage == undefined) {
callbacks.reportMessage(InfrastructureMessages.Info_FileBuiltSuccessfully({filename}));
} else {
callbacks.reportMessage(InfrastructureMessages.Info_FileNotBuiltSuccessfully({filename}));