diff --git a/common/web/types/src/util/compiler-interfaces.ts b/common/web/types/src/util/compiler-interfaces.ts index a7e2fba8416..b05883516f3 100644 --- a/common/web/types/src/util/compiler-interfaces.ts +++ b/common/web/types/src/util/compiler-interfaces.ts @@ -258,6 +258,10 @@ export enum CompilerErrorNamespace { * kmc-keyboard-info 0x9000…0x9FFF */ KeyboardInfoCompiler = 0x9000, + /** + * kmc-generate 0xA000…0xAFFF + */ + Generator = 0xA000, }; /** diff --git a/developer/build.sh b/developer/build.sh index f0b1373dff6..34fb2d2c4b1 100755 --- a/developer/build.sh +++ b/developer/build.sh @@ -30,6 +30,7 @@ builder_describe \ ":utils=src/common/web/utils Developer utils" \ ":kmcmplib=src/kmcmplib Compiler - .kmn compiler" \ ":kmc-analyze=src/kmc-analyze Compiler - Analysis Tools" \ + ":kmc-generate=src/kmc-generate Compiler - Generation Tools" \ ":kmc-keyboard-info=src/kmc-keyboard-info Compiler - .keyboard_info Module" \ ":kmc-kmn=src/kmc-kmn Compiler - .kmn to .kmx and .js Keyboard Module" \ ":kmc-ldml=src/kmc-ldml Compiler - LDML Keyboard Module" \ diff --git a/developer/src/Makefile b/developer/src/Makefile index c62127437e3..d9c762e8a91 100644 --- a/developer/src/Makefile +++ b/developer/src/Makefile @@ -64,7 +64,7 @@ server: .virtual cd $(DEVELOPER_ROOT)\src\server $(MAKE) $(TARGET) -kmc: kmc-analyze kmc-ldml kmc-keyboard-info kmc-kmn kmc-model kmc-model-info kmc-package +kmc: kmc-analyze kmc-generate kmc-ldml kmc-keyboard-info kmc-kmn kmc-model kmc-model-info kmc-package cd $(DEVELOPER_ROOT)\src\kmc $(MAKE) $(TARGET) @@ -72,6 +72,10 @@ kmc-analyze: .virtual cd $(DEVELOPER_ROOT)\src\kmc-analyze $(MAKE) $(TARGET) +kmc-generate: .virtual + cd $(DEVELOPER_ROOT)\src\kmc-generate + $(MAKE) $(TARGET) + kmc-keyboard-info: .virtual cd $(DEVELOPER_ROOT)\src\kmc-keyboard-info $(MAKE) $(TARGET) diff --git a/developer/src/README.md b/developer/src/README.md index 5d923dd4646..750ff8bca53 100644 --- a/developer/src/README.md +++ b/developer/src/README.md @@ -57,6 +57,10 @@ node-based next generation compiler, hosts kmc, (and legacy kmlmc, kmlmp) File analysis tools for Keyman files. +### src/kmc-generate - Generation tools + +Project generation tools for Keyman. + ### src/kmc-keyboard-info - Keyboard Info Compiler Builds .keyboard_info files for use on the Keyman Cloud keyboard repository diff --git a/developer/src/kmc-generate/.eslintrc.cjs b/developer/src/kmc-generate/.eslintrc.cjs new file mode 100644 index 00000000000..87b1119074c --- /dev/null +++ b/developer/src/kmc-generate/.eslintrc.cjs @@ -0,0 +1,15 @@ +module.exports = { + parserOptions: { + project: ["./tsconfig.json", "./test/tsconfig.json"], + }, + ignorePatterns: ["test/fixtures/**/*", "src/template/**/*"], + overrides: [ + { + files:"src/**/*.ts", + //TODO: enable extends: ["../../../common/web/eslint/eslintNoNodeImports.js"], + } + ], + rules: { + "prefer-const": 1, + }, +}; diff --git a/developer/src/kmc-generate/Makefile b/developer/src/kmc-generate/Makefile new file mode 100644 index 00000000000..3cf3ac055a9 --- /dev/null +++ b/developer/src/kmc-generate/Makefile @@ -0,0 +1,39 @@ +# +# Keyman Developer - kmc Keyboard Compiler Makefile +# + +!include ..\Defines.mak + +# We do configure here because parent Makefile calls this first; other +# kmc and kmc-* makefiles don't do it +build: configure .virtual + $(GIT_BASH_FOR_KEYMAN) build.sh build + +configure: .virtual + $(GIT_BASH_FOR_KEYMAN) build.sh configure + +clean: .virtual + $(GIT_BASH_FOR_KEYMAN) build.sh clean + +test: .virtual + $(GIT_BASH_FOR_KEYMAN) build.sh test + +# build.sh bundle must be run from shell as it requires a temp folder to be +# passed in. See inst/download.in.mak for instantiation. + +publish: .virtual + $(GIT_BASH_FOR_KEYMAN) build.sh publish + +signcode: + @rem nothing to do + +wrap-symbols: + @rem nothing to do + +test-manifest: + @rem nothing to do + +install: + @rem nothing to do + +!include ..\Target.mak diff --git a/developer/src/kmc-generate/build.sh b/developer/src/kmc-generate/build.sh new file mode 100755 index 00000000000..71d06516c50 --- /dev/null +++ b/developer/src/kmc-generate/build.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +## START STANDARD BUILD SCRIPT INCLUDE +# adjust relative paths as necessary +THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" +. "${THIS_SCRIPT%/*}/../../../resources/build/build-utils.sh" +## END STANDARD BUILD SCRIPT INCLUDE + +. "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" + +builder_describe "Build Keyman kmc-generate module" \ + "@/common/web/keyman-version" \ + "@/common/web/types" \ + "@/developer/src/common/web/test-helpers" \ + "configure" \ + "build" \ + "clean" \ + "test" \ + "pack build a local .tgz pack for testing" \ + "publish publish to npm" \ + "--dry-run,-n don't actually publish, just dry run" +builder_describe_outputs \ + configure /node_modules \ + build /developer/src/kmc-generate/build/src/main.js + +builder_parse "$@" + +#------------------------------------------------------------------------------------------------------------------- + +do_build() { + tsc --build + rm -rf ./build/src/template + mkdir -p ./build/src/template + cp -R ./src/template/ ./build/src/ +} + +do_test() { + eslint . + cd test + tsc -b + cd .. + c8 --reporter=lcov --reporter=text mocha "${builder_extra_params[@]}" +} + +builder_run_action clean rm -rf ./build/ ./tsconfig.tsbuildinfo +builder_run_action configure verify_npm_setup +builder_run_action build do_build +builder_run_action test do_test +builder_run_action publish builder_publish_to_npm +builder_run_action pack builder_publish_to_pack diff --git a/developer/src/kmc-generate/package.json b/developer/src/kmc-generate/package.json new file mode 100644 index 00000000000..cdcc9ee1dbd --- /dev/null +++ b/developer/src/kmc-generate/package.json @@ -0,0 +1,66 @@ +{ + "name": "@keymanapp/kmc-generate", + "description": "Keyman Developer generate module", + "keywords": [ + "keyboard", + "keyman", + "ldml", + "unicode" + ], + "type": "module", + "exports": { + ".": "./build/src/main.js" + }, + "files": [ + "/build/src/" + ], + "scripts": { + "build": "gosh ./build.sh build", + "lint": "eslint .", + "test": "gosh ./build.sh test" + }, + "author": "Marc Durdin (https://github.com/mcdurdin)", + "license": "MIT", + "bugs": { + "url": "https://github.com/keymanapp/keyman/issues" + }, + "dependencies": { + "@keymanapp/keyman-version": "*", + "@keymanapp/common-types": "*" + }, + "devDependencies": { + "@keymanapp/developer-test-helpers": "*", + "@keymanapp/resources-gosh": "*", + "@types/chai": "^4.1.7", + "@types/mocha": "^5.2.7", + "@types/node": "^20.4.1", + "@types/semver": "^7.3.12", + "c8": "^7.12.0", + "chai": "^4.3.4", + "chalk": "^2.4.2", + "mocha": "^8.4.0", + "ts-node": "^9.1.1", + "typescript": "^4.9.5" + }, + "mocha": { + "spec": "build/test/**/test-*.js", + "require": [ + "source-map-support/register" + ] + }, + "c8": { + "all": true, + "src": [ + "src/" + ], + "exclude-after-remap": true, + "exclude": [ + "test/", + "src/template/" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/keymanapp/keyman.git" + } +} diff --git a/developer/src/kmc-generate/src/abstract-generator.ts b/developer/src/kmc-generate/src/abstract-generator.ts new file mode 100644 index 00000000000..bcba390a4c7 --- /dev/null +++ b/developer/src/kmc-generate/src/abstract-generator.ts @@ -0,0 +1,125 @@ +import * as fs from 'fs'; //TODO +import { CompilerCallbacks, CompilerLogLevel } from "@keymanapp/common-types"; +import { fileURLToPath } from 'url'; + +export interface GeneratorOptions /* not inheriting from CompilerBaseOptions */ { + /** + * Reporting level to console, used by NodeCompilerCallbacks (not used in + * compiler modules; all messages are still reported to the internal log) + */ + logLevel?: CompilerLogLevel; + targets: string[]; + outPath: string; + name: string; + copyright?: string; + version: string; + languageTags: string[]; + author?: string; + icon: Boolean; + description?: string; +}; + +export class AbstractGenerator { + /** + * id for the keyboard or model, aka the basename sans extension, foldername + * for the project + */ + protected get id() { return this._id } + private _id: string; + + /** + * extension of options.copyright including copyright year + */ + protected fullCopyright: string = ''; + + /** + * identifiers for lines to include when transforming template files, filled + * by child classes + */ + protected get includedPrefixes() { return this._includedPrefixes; } + private _includedPrefixes: string[]; + + /** + * tokens to rewrite in output files + */ + protected get tokenMap() { return this._tokenMap; } + private _tokenMap: {[index:string]: string}; + + /** + * map of all files to be transformed, filled by this class and subclasses + */ + protected get filenameMap() { return this._filenameMap; } + private _filenameMap: {[index:string]: string}; + + /** + * base path for template files in this module + */ + protected get templateBasePath() { return this._templateBasePath; } + private _templateBasePath: string; + + protected static readonly SPath_Source = 'source/'; + protected static readonly SFile_WelcomeHTM = `${this.SPath_Source}welcome.htm`; + protected static readonly SFile_ReadmeHTM = `${this.SPath_Source}readme.htm`; + protected static readonly SFile_HistoryMD = 'HISTORY.md'; + protected static readonly SFile_LicenseMD = 'LICENSE.md'; + protected static readonly SFile_ReadmeMD = 'README.md'; + + constructor(protected callbacks: CompilerCallbacks, protected options: GeneratorOptions) {} + + async init(id: string): Promise { + this._id = id; + this._includedPrefixes = []; + this._filenameMap = {}; + this._tokenMap = {}; + + this._templateBasePath = this.callbacks.path.join( + this.callbacks.path.dirname(fileURLToPath(import.meta.url)), + 'template' + ) + ; + + + // These files are currently always included, for all project types + this.filenameMap[AbstractGenerator.SFile_WelcomeHTM] = AbstractGenerator.SFile_WelcomeHTM; + this.filenameMap[AbstractGenerator.SFile_ReadmeHTM] = AbstractGenerator.SFile_ReadmeHTM; + this.filenameMap[AbstractGenerator.SFile_HistoryMD] = AbstractGenerator.SFile_HistoryMD; + this.filenameMap[AbstractGenerator.SFile_LicenseMD] = AbstractGenerator.SFile_LicenseMD; + this.filenameMap[AbstractGenerator.SFile_ReadmeMD] = AbstractGenerator.SFile_ReadmeMD; + return true; + } + + /** + * Create folders and verify accessibility + * @returns + */ + async run(): Promise { + + if(this.targetPathExists()) { + // TODO yammer + console.error('target path exists'); + return false; + } + if(!this.createTargetPath()) { + // TODO yammer + console.error('failed creating target path'); + return false; + } + + return true; + } + + protected readonly targetPath = () => this.callbacks.path.join(this.options.outPath,this.id); + + private readonly targetPathExists = () => this.callbacks.fs.existsSync(this.targetPath()); + + private createTargetPath() { + /*TODO: this.callbacks.*/fs.mkdirSync(this.callbacks.path.join(this.targetPath(),'source'), {recursive:true}); + /*if(!) { + // TODO log exception + // raise EKeyboardProjectTemplate.Create('Could not create destination path '+BasePath+ID); + return false; + }*/ + return true; + } +} + diff --git a/developer/src/kmc-generate/src/basic-generator.ts b/developer/src/kmc-generate/src/basic-generator.ts new file mode 100644 index 00000000000..3f665979737 --- /dev/null +++ b/developer/src/kmc-generate/src/basic-generator.ts @@ -0,0 +1,109 @@ +import { KeymanTargets } from "@keymanapp/common-types"; +import KEYMAN_VERSION from "@keymanapp/keyman-version"; +import { AbstractGenerator } from "./abstract-generator.js"; + +export class BasicGenerator extends AbstractGenerator { + + protected templatePath: string; + + override async run(): Promise { + this.fullCopyright = '© ' + new Date().getFullYear().toString() + ' ' + this.options.copyright; + this.options.copyright = '© ' + this.options.copyright; + this.options.languageTags = this.options.languageTags.length + ? this.options.languageTags.map(tag => new Intl.Locale(tag).minimize().toString()) + : ['en']; + + this.tokenMap['$NAME'] = this.options.name; + this.tokenMap['$ID'] = this.id; + this.tokenMap['$KEYMANVERSION'] = KEYMAN_VERSION.VERSION+'.0'; + this.tokenMap['$VERSION'] = this.options.version; + this.tokenMap['$COPYRIGHT'] = this.options.copyright; + this.tokenMap['$FULLCOPYRIGHT'] = this.fullCopyright; + this.tokenMap['$AUTHOR'] = this.options.author ?? ''; + this.tokenMap['$TARGETS'] = this.options.targets.join(' ') ?? 'any'; //TODO: validate targets + this.tokenMap['$DESCRIPTION'] = this.options.description; + this.tokenMap['$DATE'] = new Date().toString(); //TODO: only yyyy-mm-dd + this.tokenMap['$PACKAGE_LANGUAGES'] = this.generateLanguageListForPackage(); + this.tokenMap['$PLATFORMS_DOTLIST_README'] = this.getPlatformDotListForReadme(); + + if(!await super.run()) { + return false; + } + + return this.transformAll(); + } + + private getLanguageName(tag: string) { + // TODO: probably need to use langtags.json + return new Intl.Locale(tag).language; + } + + private generateLanguageListForPackage(): string { + // + // Central Khmer (Khmer, Cambodia) + // + const langs = this.options.languageTags.length ? this.options.languageTags : ['en']; + const result = + ` \n`+ + langs.map(tag => ` ${this.getLanguageName(tag)}`).join('\n')+ + ` `; + return result; + } + + private getPlatformDotListForReadme() { + const t = this.options.targets; + const result = KeymanTargets.AllKeymanTargets + .filter(target => + target != KeymanTargets.KeymanTarget.any && + (t.includes(target) || t.includes(KeymanTargets.KeymanTarget.any)) + ) + .map(target => ` * ${KeymanTargets.SKeymanTargetNames[target]}`) + .join('\n'); + return result; + } + + private readonly transformAll = () => Object.keys(this.filenameMap).every(src => this.transform(src, this.filenameMap[src])); + + private transform(sourceFile: string, destFile: string = '') { + destFile = this.callbacks.path.join(this.options.outPath, this.id, destFile == '' ? sourceFile : destFile); + sourceFile = this.callbacks.path.join(this.templateBasePath, this.templatePath, sourceFile); + + if(!this.callbacks.fs.existsSync(sourceFile)) { + // internal error -- source file should exist + throw new Error(`sourcepath ${sourceFile} does not exist`); + } + + const sourceData = this.callbacks.loadFile(sourceFile); + if(sourceData == null) { + // internal error -- source file should be readable + throw new Error(`source file ${sourceFile} could not be read`); + } + const template = new TextDecoder('utf-8').decode(sourceData).replace(/\r/g, '').split('\n'); + + const source: string[] = []; + + // Filter out unused lines + + for(const line of template) { + if(!line.match(/\$[A-Z0-9_-]+:/i)) { + source.push(line); + } else { + const tokens = line.split(':', 2); + if(this.includedPrefixes.includes(tokens[0].substring(1))) { + source.push(tokens[1]); + } + } + } + + // Replace all tokens + + // TODO: consider using a map + let dest = source.join('\n'); + Object.keys(this.tokenMap).forEach(token => dest = dest.replaceAll(token, this.tokenMap[token])); + + // TODO: this needs to be pulled out of here + this.callbacks.fs.writeFileSync(destFile, new TextEncoder().encode(dest)); + + return true; + } +} diff --git a/developer/src/kmc-generate/src/generator-messages.ts b/developer/src/kmc-generate/src/generator-messages.ts new file mode 100644 index 00000000000..b3b85de2a3d --- /dev/null +++ b/developer/src/kmc-generate/src/generator-messages.ts @@ -0,0 +1,17 @@ +import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageSpec as m } from "@keymanapp/common-types"; + +const Namespace = CompilerErrorNamespace.Generator; +const SevInfo = CompilerErrorSeverity.Info | Namespace; +// const SevHint = CompilerErrorSeverity.Hint | Namespace; +// const SevWarn = CompilerErrorSeverity.Warn | Namespace; +// const SevError = CompilerErrorSeverity.Error | Namespace; +const SevFatal = CompilerErrorSeverity.Fatal | Namespace; + +export class GeneratorMessages { + static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException, null, o.e ?? 'unknown error'); + static FATAL_UnexpectedException = SevFatal | 0x0001; + + static Info_GeneratingProject = (o:{type: string, name: string}) => m(this.INFO_GeneratingProject, + `Scanning ${o.type} file ${o.name}`); + static INFO_GeneratingProject = SevInfo | 0x0002; +}; diff --git a/developer/src/kmc-generate/src/keyman-keyboard-generator.ts b/developer/src/kmc-generate/src/keyman-keyboard-generator.ts new file mode 100644 index 00000000000..5ba8d9e03b9 --- /dev/null +++ b/developer/src/kmc-generate/src/keyman-keyboard-generator.ts @@ -0,0 +1,91 @@ +import { KeymanFileTypes, KeymanTargets } from '@keymanapp/common-types'; +import { BasicGenerator } from './basic-generator.js'; + +/** + * Future: we probably want to have a more abstract implementation so that we + * can use this for both generate and clone keyboard? + * + * So we have a structure of an entire project passed into to a writer. Even if + * we can't cleanly reuse at least we can copy the code more easily and it will + * be more maintainable. + * + * But for now we are working with plain text approach + */ + +export class KeymanKeyboardGenerator extends BasicGenerator { + static readonly SFile_Keyboard = 'keyboard'; + static readonly SFile_KeyboardKMN = `${this.SPath_Source}${this.SFile_Keyboard}${KeymanFileTypes.Source.KeymanKeyboard}`; + static readonly SFile_KeyboardKPS = `${this.SPath_Source}${this.SFile_Keyboard}${KeymanFileTypes.Source.Package}`; + static readonly SFile_KeyboardKVKS = `${this.SPath_Source}${this.SFile_Keyboard}${KeymanFileTypes.Source.VisualKeyboard}`; + static readonly SFile_TouchLayout = `${this.SPath_Source}${this.SFile_Keyboard}${KeymanFileTypes.Source.TouchLayout}`; + static readonly SFile_Project = `${this.SFile_Keyboard}${KeymanFileTypes.Source.Project}`; + + override async init(id: string): Promise { + if(!await super.init(id)) { + return false; + } + + this.templatePath = 'kmn-keyboard'; + return true; + } + + override async run(): Promise { + this.filenameMap[KeymanKeyboardGenerator.SFile_Project] = this.id+KeymanFileTypes.Source.Project; + + if(this.hasKVKS()) { + this.includedPrefixes.push('KVKS'); + this.filenameMap[KeymanKeyboardGenerator.SFile_KeyboardKVKS] = KeymanKeyboardGenerator.SPath_Source+this.id+KeymanFileTypes.Source.VisualKeyboard; + } + + if(this.hasWeb()) { + this.includedPrefixes.push('Web'); + } + + if(this.hasTouchLayout()) { + this.includedPrefixes.push('TouchLayout'); + this.filenameMap[KeymanKeyboardGenerator.SFile_TouchLayout] = KeymanKeyboardGenerator.SPath_Source+this.id+KeymanFileTypes.Source.TouchLayout; + } + + if(this.hasIcon()) { + this.includedPrefixes.push('Icon'); + } + + if(this.hasKMX()) { + this.includedPrefixes.push('KMX'); + } + + this.filenameMap[KeymanKeyboardGenerator.SFile_KeyboardKMN] = KeymanKeyboardGenerator.SPath_Source+this.id+KeymanFileTypes.Source.KeymanKeyboard; + this.filenameMap[KeymanKeyboardGenerator.SFile_KeyboardKPS] = KeymanKeyboardGenerator.SPath_Source+this.id+KeymanFileTypes.Source.Package; + + if(!await super.run()) { + return false; + } + + // Special case for creating icon, run after successful creation of other + // project bits and pieces + if(this.hasIcon()) { + this.writeIcon(); + } + + return true; + } + + private readonly targetIncludes = (targets: KeymanTargets.KeymanTarget[]) => { + const tx: string[] = [...targets, KeymanTargets.KeymanTarget.any]; + return this.options.targets.some(t => tx.includes(t)); + } + + private readonly hasKVKS = () => this.targetIncludes(KeymanTargets.KeymanTargetsUsingKVK); + private readonly hasWeb = () => this.targetIncludes(KeymanTargets.KMWKeymanTargets); + private readonly hasKMX = () => this.targetIncludes(KeymanTargets.KMXKeymanTargets); + private readonly hasTouchLayout = () => this.targetIncludes(KeymanTargets.TouchKeymanTargets); + + // TODO, once writeIcon is implemented: + // hasIcon = () => this.options.icon && this.targetIncludes(KeymanTargets.KMXKeymanTargets); + private readonly hasIcon = () => false; + + private writeIcon() { + // TODO: this will require some effort + // proposal: generate 16x16 icon with 2-3 letters. Following TKeyboardIconGenerator.GenerateIcon + } +} diff --git a/developer/src/kmc-generate/src/ldml-keyboard-generator.ts b/developer/src/kmc-generate/src/ldml-keyboard-generator.ts new file mode 100644 index 00000000000..a5eee473654 --- /dev/null +++ b/developer/src/kmc-generate/src/ldml-keyboard-generator.ts @@ -0,0 +1,36 @@ +import { KeymanFileTypes } from '@keymanapp/common-types'; +import { BasicGenerator } from './basic-generator.js'; + +export class LdmlKeyboardGenerator extends BasicGenerator { + protected static readonly SFile_Keyboard = 'keyboard'; + static readonly SFile_KeyboardXML = `${this.SPath_Source}${this.SFile_Keyboard}${KeymanFileTypes.Source.LdmlKeyboard}`; + static readonly SFile_Package = `${this.SPath_Source}${this.SFile_Keyboard}${KeymanFileTypes.Source.Package}`; + static readonly SFile_Project = `${this.SFile_Keyboard}${KeymanFileTypes.Source.Project}`; + + override async init(id: string): Promise { + if(!await super.init(id)) { + return false; + } + + this.templatePath = 'ldml-keyboard'; + return true; + } + + override async run(): Promise { + this.filenameMap[LdmlKeyboardGenerator.SFile_Project] = this.id+KeymanFileTypes.Source.Project; + this.filenameMap[LdmlKeyboardGenerator.SFile_KeyboardXML] = LdmlKeyboardGenerator.SPath_Source+this.id+KeymanFileTypes.Source.LdmlKeyboard; + this.filenameMap[LdmlKeyboardGenerator.SFile_Package] = LdmlKeyboardGenerator.SPath_Source+this.id+KeymanFileTypes.Source.Package; + + this.tokenMap['$LANG_TAG'] = this.generateFirstLangTag(); + this.tokenMap['$ADDITIONAL_LANG_TAGS'] = this.generateAlternateLangTags(); + + if(!await super.run()) { + return false; + } + + return true; + } + + private readonly generateFirstLangTag = () => '???'; //TODO + private readonly generateAlternateLangTags = () => '???'; //TODO +} diff --git a/developer/src/kmc-generate/src/lexical-model-generator.ts b/developer/src/kmc-generate/src/lexical-model-generator.ts new file mode 100644 index 00000000000..9d4bb539338 --- /dev/null +++ b/developer/src/kmc-generate/src/lexical-model-generator.ts @@ -0,0 +1,49 @@ +import { KeymanFileTypes } from '@keymanapp/common-types'; +import { BasicGenerator } from './basic-generator.js'; + +/** + * Future: we probably want to have a more abstract implementation so that we + * can use this for both generate and clone keyboard? + * + * So we have a structure of an entire project passed into to a writer. Even if + * we can't cleanly reuse at least we can copy the code more easily and it will + * be more maintainable. + * + * But for now we are working with plain text approach + */ + +export class LexicalModelGenerator extends BasicGenerator { + static readonly SFile_Model = 'model'; + static readonly SFile_ModelTs = `${this.SPath_Source}${this.SFile_Model}.ts`; + static readonly SFile_WordlistTsv = `${this.SPath_Source}wordlist.tsv`; + static readonly SFile_Project = `${this.SFile_Model}${KeymanFileTypes.Source.Project}`; + static readonly SFile_ModelInfo = `${this.SFile_Model}${KeymanFileTypes.Binary.ModelInfo}`; + static readonly SFile_Package = `${this.SPath_Source}${this.SFile_Model}${KeymanFileTypes.Source.Package}`; + + override async init(id: string): Promise { + if(!await super.init(id)) { + return false; + } + + this.templatePath = 'wordlist-lexical-model'; + return true; + } + + override async run(): Promise { + this.filenameMap[LexicalModelGenerator.SFile_Project] = this.id+KeymanFileTypes.Source.Project; + this.filenameMap[LexicalModelGenerator.SFile_ModelInfo] = this.id+KeymanFileTypes.Binary.ModelInfo; + this.filenameMap[LexicalModelGenerator.SFile_ModelTs] = LexicalModelGenerator.SPath_Source+this.id+KeymanFileTypes.Source.Model; + this.filenameMap[LexicalModelGenerator.SFile_WordlistTsv] = LexicalModelGenerator.SFile_WordlistTsv; + this.filenameMap[LexicalModelGenerator.SFile_Package] = LexicalModelGenerator.SPath_Source+this.id+KeymanFileTypes.Source.Package; + this.tokenMap['$LANGUAGES_MODEL_INFO'] = this.generateLanguageTagListForModelInfo(); + + if(!await super.run()) { + return false; + } + + return true; + } + + private readonly generateLanguageTagListForModelInfo = (): string => + this.options.languageTags.map(tag => (new Intl.Locale(tag)).minimize()).join(', '); +} diff --git a/developer/src/kmc-generate/src/main.ts b/developer/src/kmc-generate/src/main.ts new file mode 100644 index 00000000000..be2fcdf4a6d --- /dev/null +++ b/developer/src/kmc-generate/src/main.ts @@ -0,0 +1,5 @@ +export { AbstractGenerator, GeneratorOptions } from "./abstract-generator.js"; +export { KeymanKeyboardGenerator } from "./keyman-keyboard-generator.js"; +export { LexicalModelGenerator } from "./lexical-model-generator.js"; +export { LdmlKeyboardGenerator } from "./ldml-keyboard-generator.js"; +export { GeneratorMessages } from "./generator-messages.js"; diff --git a/developer/src/kmc-generate/src/template/kmn-keyboard/HISTORY.md b/developer/src/kmc-generate/src/template/kmn-keyboard/HISTORY.md new file mode 100644 index 00000000000..9a605b69a3a --- /dev/null +++ b/developer/src/kmc-generate/src/template/kmn-keyboard/HISTORY.md @@ -0,0 +1,6 @@ +$NAME Change History +==================== + +$VERSION ($DATE) +---------------- +* Created by $AUTHOR diff --git a/developer/src/kmc-generate/src/template/kmn-keyboard/LICENSE.md b/developer/src/kmc-generate/src/template/kmn-keyboard/LICENSE.md new file mode 100644 index 00000000000..bb0daf3447f --- /dev/null +++ b/developer/src/kmc-generate/src/template/kmn-keyboard/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +$FULLCOPYRIGHT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/developer/src/kmc-generate/src/template/kmn-keyboard/README.md b/developer/src/kmc-generate/src/template/kmn-keyboard/README.md new file mode 100644 index 00000000000..56c60908394 --- /dev/null +++ b/developer/src/kmc-generate/src/template/kmn-keyboard/README.md @@ -0,0 +1,18 @@ +$NAME keyboard +============== + +Description +----------- +$DESCRIPTION + +Links +----- +Keyboard Homepage: https://keyman.com/keyboards/$ID + +Copyright +--------- +See [LICENSE.md](LICENSE.md) + +Supported Platforms +------------------- +$PLATFORMS_DOTLIST_README diff --git a/developer/src/kmc-generate/src/template/kmn-keyboard/keyboard.kpj b/developer/src/kmc-generate/src/template/kmn-keyboard/keyboard.kpj new file mode 100644 index 00000000000..7ed1ac72df6 --- /dev/null +++ b/developer/src/kmc-generate/src/template/kmn-keyboard/keyboard.kpj @@ -0,0 +1,8 @@ + + + + 2.0 + True + True + + diff --git a/developer/src/kmc-generate/src/template/kmn-keyboard/source/keyboard.keyman-touch-layout b/developer/src/kmc-generate/src/template/kmn-keyboard/source/keyboard.keyman-touch-layout new file mode 100644 index 00000000000..222b51a9481 --- /dev/null +++ b/developer/src/kmc-generate/src/template/kmn-keyboard/source/keyboard.keyman-touch-layout @@ -0,0 +1,655 @@ +{ + "phone": { + "font": "Tahoma", + "layer": [ + { + "id": "default", + "row": [ + { + "id": 1, + "key": [ + { + "id": "K_Q", + "text": "q" + }, + { + "id": "K_W", + "text": "w" + }, + { + "id": "K_E", + "text": "e" + }, + { + "id": "K_R", + "text": "r" + }, + { + "id": "K_T", + "text": "t" + }, + { + "id": "K_Y", + "text": "y" + }, + { + "id": "K_U", + "text": "u" + }, + { + "id": "K_I", + "text": "i" + }, + { + "id": "K_O", + "text": "o" + }, + { + "id": "K_P", + "text": "p" + } + ] + }, + { + "id": 2, + "key": [ + { + "id": "K_A", + "text": "a", + "pad": "50" + }, + { + "id": "K_S", + "text": "s" + }, + { + "id": "K_D", + "text": "d" + }, + { + "id": "K_F", + "text": "f" + }, + { + "id": "K_G", + "text": "g" + }, + { + "id": "K_H", + "text": "h" + }, + { + "id": "K_J", + "text": "j" + }, + { + "id": "K_K", + "text": "k" + }, + { + "id": "K_L", + "text": "l" + }, + { + "text": "", + "width": "10", + "sp": "10" + } + ] + }, + { + "id": 3, + "key": [ + { + "id": "K_SHIFT", + "text": "*Shift*", + "sp": "1", + "nextlayer": "shift" + }, + { + "id": "K_Z", + "text": "z" + }, + { + "id": "K_X", + "text": "x" + }, + { + "id": "K_C", + "text": "c" + }, + { + "id": "K_V", + "text": "v" + }, + { + "id": "K_B", + "text": "b" + }, + { + "id": "K_N", + "text": "n" + }, + { + "id": "K_M", + "text": "m" + }, + { + "id": "K_PERIOD", + "text": ".", + "sk": [ + { + "text": ",", + "id": "K_COMMA" + }, + { + "text": "!", + "id": "K_1", + "layer": "shift" + }, + { + "text": "?", + "id": "K_SLASH", + "layer": "shift" + }, + { + "text": "'", + "id": "K_QUOTE" + }, + { + "text": "\"", + "id": "K_QUOTE", + "layer": "shift" + }, + { + "text": "\\", + "id": "K_BKSLASH" + }, + { + "text": ":", + "id": "K_COLON", + "layer": "shift" + }, + { + "text": ";", + "id": "K_COLON" + } + ] + }, + { + "id": "K_BKSP", + "text": "*BkSp*", + "width": "100", + "sp": "1" + } + ] + }, + { + "id": 4, + "key": [ + { + "id": "K_NUMLOCK", + "text": "*123*", + "width": "150", + "sp": "1", + "nextlayer": "numeric" + }, + { + "id": "K_LOPT", + "text": "*Menu*", + "width": "120", + "sp": "1" + }, + { + "id": "K_SPACE", + "text": "", + "width": "610", + "sp": "0" + }, + { + "id": "K_ENTER", + "text": "*Enter*", + "width": "150", + "sp": "1" + } + ] + } + ] + }, + { + "id": "shift", + "row": [ + { + "id": 1, + "key": [ + { + "id": "K_Q", + "text": "Q" + }, + { + "id": "K_W", + "text": "W" + }, + { + "id": "K_E", + "text": "E" + }, + { + "id": "K_R", + "text": "R" + }, + { + "id": "K_T", + "text": "T" + }, + { + "id": "K_Y", + "text": "Y" + }, + { + "id": "K_U", + "text": "U" + }, + { + "id": "K_I", + "text": "I" + }, + { + "id": "K_O", + "text": "O" + }, + { + "id": "K_P", + "text": "P" + } + ] + }, + { + "id": 2, + "key": [ + { + "id": "K_A", + "text": "A", + "pad": "50" + }, + { + "id": "K_S", + "text": "S" + }, + { + "id": "K_D", + "text": "D" + }, + { + "id": "K_F", + "text": "F" + }, + { + "id": "K_G", + "text": "G" + }, + { + "id": "K_H", + "text": "H" + }, + { + "id": "K_J", + "text": "J" + }, + { + "id": "K_K", + "text": "K" + }, + { + "id": "K_L", + "text": "L" + }, + { + "text": "", + "width": "10", + "sp": "10" + } + ] + }, + { + "id": 3, + "key": [ + { + "id": "K_SHIFT", + "text": "*Shift*", + "sp": "2", + "nextlayer": "default" + }, + { + "id": "K_Z", + "text": "Z" + }, + { + "id": "K_X", + "text": "X" + }, + { + "id": "K_C", + "text": "C" + }, + { + "id": "K_V", + "text": "V" + }, + { + "id": "K_B", + "text": "B" + }, + { + "id": "K_N", + "text": "N" + }, + { + "id": "K_M", + "text": "M" + }, + { + "id": "K_PERIOD", + "text": ".", + "layer": "default", + "sk": [ + { + "text": ",", + "id": "K_COMMA", + "layer": "default" + }, + { + "text": "!", + "id": "K_1", + "layer": "shift" + }, + { + "text": "?", + "id": "K_SLASH", + "layer": "shift" + }, + { + "text": "'", + "id": "K_QUOTE", + "layer": "default" + }, + { + "text": "\"", + "id": "K_QUOTE", + "layer": "shift" + }, + { + "text": "\\", + "id": "K_BKSLASH", + "layer": "default" + }, + { + "text": ":", + "id": "K_COLON", + "layer": "shift" + }, + { + "text": ";", + "id": "K_COLON", + "layer": "default" + } + ] + }, + { + "id": "K_BKSP", + "text": "*BkSp*", + "sp": "1" + } + ] + }, + { + "id": 4, + "key": [ + { + "id": "K_NUMLOCK", + "text": "*123*", + "width": "150", + "sp": "1", + "nextlayer": "numeric" + }, + { + "id": "K_LOPT", + "text": "*Menu*", + "width": "120", + "sp": "1" + }, + { + "id": "K_SPACE", + "text": "", + "width": "610", + "sp": "0" + }, + { + "id": "K_ENTER", + "text": "*Enter*", + "width": "150", + "sp": "1" + } + ] + } + ] + }, + { + "id": "numeric", + "row": [ + { + "id": 1, + "key": [ + { + "id": "K_1", + "text": "1" + }, + { + "id": "K_2", + "text": "2" + }, + { + "id": "K_3", + "text": "3" + }, + { + "id": "K_4", + "text": "4" + }, + { + "id": "K_5", + "text": "5" + }, + { + "id": "K_6", + "text": "6" + }, + { + "id": "K_7", + "text": "7" + }, + { + "id": "K_8", + "text": "8" + }, + { + "id": "K_9", + "text": "9" + }, + { + "id": "K_0", + "text": "0" + } + ] + }, + { + "id": 2, + "key": [ + { + "id": "K_4", + "layer": "shift", + "text": "$", + "pad": "50" + }, + { + "id": "K_2", + "layer": "shift", + "text": "@" + }, + { + "id": "K_3", + "layer": "shift", + "text": "#" + }, + { + "id": "K_5", + "layer": "shift", + "text": "%" + }, + { + "id": "K_7", + "layer": "shift", + "text": "&" + }, + { + "id": "K_HYPHEN", + "layer": "shift", + "text": "_" + }, + { + "id": "K_EQUAL", + "text": "=", + "layer": "default" + }, + { + "id": "K_BKSLASH", + "layer": "shift", + "text": "|" + }, + { + "id": "K_BKSLASH", + "text": "\\", + "layer": "default" + }, + { + "text": "", + "width": "10", + "sp": "10" + } + ] + }, + { + "id": 3, + "key": [ + { + "id": "K_LBRKT", + "text": "[", + "pad": "110", + "sk": [ + { + "id": "U_00AB", + "text": "\u00AB" + }, + { + "id": "K_COMMA", + "text": "<", + "layer": "shift" + }, + { + "id": "K_LBRKT", + "text": "{", + "layer": "shift" + } + ] + }, + { + "id": "K_9", + "layer": "shift", + "text": "(" + }, + { + "id": "K_0", + "layer": "shift", + "text": ")" + }, + { + "id": "K_RBRKT", + "text": "]", + "sk": [ + { + "id": "U_00BB", + "text": "\u00BB" + }, + { + "id": "K_PERIOD", + "text": ">", + "layer": "shift" + }, + { + "id": "K_RBRKT", + "text": "}", + "layer": "shift" + } + ] + }, + { + "id": "K_EQUAL", + "layer": "shift", + "text": "+" + }, + { + "id": "K_HYPHEN", + "text": "-" + }, + { + "id": "K_8", + "layer": "shift", + "text": "*" + }, + { + "id": "K_SLASH", + "text": "/" + }, + { + "id": "K_BKSP", + "text": "*BkSp*", + "width": "100", + "sp": "1" + } + ] + }, + { + "id": 4, + "key": [ + { + "id": "K_LOWER", + "text": "*abc*", + "width": "150", + "sp": "1", + "nextlayer": "default" + }, + { + "id": "K_LOPT", + "text": "*Menu*", + "width": "120", + "sp": "1" + }, + { + "id": "K_SPACE", + "text": "", + "width": "610", + "sp": "0" + }, + { + "id": "K_ENTER", + "text": "*Enter*", + "width": "150", + "sp": "1" + } + ] + } + ] + } + ] + } +} diff --git a/developer/src/kmc-generate/src/template/kmn-keyboard/source/keyboard.kmn b/developer/src/kmc-generate/src/template/kmn-keyboard/source/keyboard.kmn new file mode 100644 index 00000000000..589104db35b --- /dev/null +++ b/developer/src/kmc-generate/src/template/kmn-keyboard/source/keyboard.kmn @@ -0,0 +1,14 @@ +c keyboard generated from template at $DATE +c with name "$NAME" +store(&NAME) '$NAME' +store(©RIGHT) '$COPYRIGHT' +store(&VERSION) '10.0' +store(&KEYBOARDVERSION) '$VERSION' +store(&TARGETS) '$TARGETS' +$Icon:store(&BITMAP) '$ID.ico' +$KVKS:store(&VISUALKEYBOARD) '$ID.kvks' +$TouchLayout:store(&LAYOUTFILE) '$ID.keyman-touch-layout' + +begin Unicode > use(main) + +group(main) using keys diff --git a/developer/src/kmc-generate/src/template/kmn-keyboard/source/keyboard.kps b/developer/src/kmc-generate/src/template/kmn-keyboard/source/keyboard.kps new file mode 100644 index 00000000000..808daac0d21 --- /dev/null +++ b/developer/src/kmc-generate/src/template/kmn-keyboard/source/keyboard.kps @@ -0,0 +1,65 @@ + + + + $KEYMANVERSION + 7.0 + + + readme.htm + ..\LICENSE.md + welcome.htm + + + + $NAME + $COPYRIGHT + $AUTHOR + $DESCRIPTION + + +$KMX: +$KMX: ..\build\$ID.kmx +$KMX: +$KMX: 0 +$KMX: .kmx +$KMX: +$Web: +$Web: ..\build\$ID.js +$Web: +$Web: 0 +$Web: .js +$Web: +$KVKS: +$KVKS: ..\build\$ID.kvk +$KVKS: +$KVKS: 0 +$KVKS: .kvk +$KVKS: + + welcome.htm + + 0 + .htm + + + readme.htm + + 0 + .htm + + + ..\LICENSE.md + + 0 + .md + + + + + $NAME + $ID + $VERSION +$PACKAGE_LANGUAGES + + + diff --git a/developer/src/kmc-generate/src/template/kmn-keyboard/source/keyboard.kvks b/developer/src/kmc-generate/src/template/kmn-keyboard/source/keyboard.kvks new file mode 100644 index 00000000000..087598da102 --- /dev/null +++ b/developer/src/kmc-generate/src/template/kmn-keyboard/source/keyboard.kvks @@ -0,0 +1,8 @@ + + +
+ 10.0 + $ID + +
+
diff --git a/developer/src/kmc-generate/src/template/kmn-keyboard/source/readme.htm b/developer/src/kmc-generate/src/template/kmn-keyboard/source/readme.htm new file mode 100644 index 00000000000..041cf910315 --- /dev/null +++ b/developer/src/kmc-generate/src/template/kmn-keyboard/source/readme.htm @@ -0,0 +1,24 @@ + + + + + + $NAME + + + + +

$NAME

+ +

+ $DESCRIPTION +

+ +

$COPYRIGHT

+ + + diff --git a/developer/src/kmc-generate/src/template/kmn-keyboard/source/welcome.htm b/developer/src/kmc-generate/src/template/kmn-keyboard/source/welcome.htm new file mode 100644 index 00000000000..c003dc2fbf1 --- /dev/null +++ b/developer/src/kmc-generate/src/template/kmn-keyboard/source/welcome.htm @@ -0,0 +1,26 @@ + + + + + + Start Using $NAME + + + + +

Start Using $NAME

+ +

+ $DESCRIPTION +

+ +

Keyboard Layout

+ + + + + \ No newline at end of file diff --git a/developer/src/kmc-generate/src/template/ldml-keyboard/HISTORY.md b/developer/src/kmc-generate/src/template/ldml-keyboard/HISTORY.md new file mode 100644 index 00000000000..9a605b69a3a --- /dev/null +++ b/developer/src/kmc-generate/src/template/ldml-keyboard/HISTORY.md @@ -0,0 +1,6 @@ +$NAME Change History +==================== + +$VERSION ($DATE) +---------------- +* Created by $AUTHOR diff --git a/developer/src/kmc-generate/src/template/ldml-keyboard/LICENSE.md b/developer/src/kmc-generate/src/template/ldml-keyboard/LICENSE.md new file mode 100644 index 00000000000..bb0daf3447f --- /dev/null +++ b/developer/src/kmc-generate/src/template/ldml-keyboard/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +$FULLCOPYRIGHT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/developer/src/kmc-generate/src/template/ldml-keyboard/README.md b/developer/src/kmc-generate/src/template/ldml-keyboard/README.md new file mode 100644 index 00000000000..56c60908394 --- /dev/null +++ b/developer/src/kmc-generate/src/template/ldml-keyboard/README.md @@ -0,0 +1,18 @@ +$NAME keyboard +============== + +Description +----------- +$DESCRIPTION + +Links +----- +Keyboard Homepage: https://keyman.com/keyboards/$ID + +Copyright +--------- +See [LICENSE.md](LICENSE.md) + +Supported Platforms +------------------- +$PLATFORMS_DOTLIST_README diff --git a/developer/src/kmc-generate/src/template/ldml-keyboard/keyboard.kpj b/developer/src/kmc-generate/src/template/ldml-keyboard/keyboard.kpj new file mode 100644 index 00000000000..7ed1ac72df6 --- /dev/null +++ b/developer/src/kmc-generate/src/template/ldml-keyboard/keyboard.kpj @@ -0,0 +1,8 @@ + + + + 2.0 + True + True + + diff --git a/developer/src/kmc-generate/src/template/ldml-keyboard/source/keyboard.kps b/developer/src/kmc-generate/src/template/ldml-keyboard/source/keyboard.kps new file mode 100644 index 00000000000..a6ca9e6d2e8 --- /dev/null +++ b/developer/src/kmc-generate/src/template/ldml-keyboard/source/keyboard.kps @@ -0,0 +1,59 @@ + + + + $KEYMANVERSION + 7.0 + + + readme.htm + ..\LICENSE.md + welcome.htm + + + + $NAME + $COPYRIGHT + $AUTHOR + $DESCRIPTION + + + + ..\build\$ID.kmx + + 0 + .kmx + + + ..\build\$ID.kvk + + 0 + .kvk + + + welcome.htm + + 0 + .htm + + + readme.htm + + 0 + .htm + + + ..\LICENSE.md + + 0 + .md + + + + + $NAME + $ID + $VERSION +$PACKAGE_LANGUAGES + + + diff --git a/developer/src/kmc-generate/src/template/ldml-keyboard/source/keyboard.xml b/developer/src/kmc-generate/src/template/ldml-keyboard/source/keyboard.xml new file mode 100644 index 00000000000..1813fc54ad4 --- /dev/null +++ b/developer/src/kmc-generate/src/template/ldml-keyboard/source/keyboard.xml @@ -0,0 +1,40 @@ + + + + +$ADDITIONAL_LANG_TAGS + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/developer/src/kmc-generate/src/template/ldml-keyboard/source/readme.htm b/developer/src/kmc-generate/src/template/ldml-keyboard/source/readme.htm new file mode 100644 index 00000000000..041cf910315 --- /dev/null +++ b/developer/src/kmc-generate/src/template/ldml-keyboard/source/readme.htm @@ -0,0 +1,24 @@ + + + + + + $NAME + + + + +

$NAME

+ +

+ $DESCRIPTION +

+ +

$COPYRIGHT

+ + + diff --git a/developer/src/kmc-generate/src/template/ldml-keyboard/source/welcome.htm b/developer/src/kmc-generate/src/template/ldml-keyboard/source/welcome.htm new file mode 100644 index 00000000000..c003dc2fbf1 --- /dev/null +++ b/developer/src/kmc-generate/src/template/ldml-keyboard/source/welcome.htm @@ -0,0 +1,26 @@ + + + + + + Start Using $NAME + + + + +

Start Using $NAME

+ +

+ $DESCRIPTION +

+ +

Keyboard Layout

+ + + + + \ No newline at end of file diff --git a/developer/src/kmc-generate/src/template/wordlist-lexical-model/HISTORY.md b/developer/src/kmc-generate/src/template/wordlist-lexical-model/HISTORY.md new file mode 100644 index 00000000000..9a605b69a3a --- /dev/null +++ b/developer/src/kmc-generate/src/template/wordlist-lexical-model/HISTORY.md @@ -0,0 +1,6 @@ +$NAME Change History +==================== + +$VERSION ($DATE) +---------------- +* Created by $AUTHOR diff --git a/developer/src/kmc-generate/src/template/wordlist-lexical-model/LICENSE.md b/developer/src/kmc-generate/src/template/wordlist-lexical-model/LICENSE.md new file mode 100644 index 00000000000..bb0daf3447f --- /dev/null +++ b/developer/src/kmc-generate/src/template/wordlist-lexical-model/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +$FULLCOPYRIGHT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/developer/src/kmc-generate/src/template/wordlist-lexical-model/README.md b/developer/src/kmc-generate/src/template/wordlist-lexical-model/README.md new file mode 100644 index 00000000000..c94a8f66b8a --- /dev/null +++ b/developer/src/kmc-generate/src/template/wordlist-lexical-model/README.md @@ -0,0 +1,17 @@ +$NAME lexical model +=================== + +Description +----------- +$DESCRIPTION + +Links +----- + +Copyright +--------- +See [LICENSE.md](LICENSE.md) + +Supported Platforms +------------------- +$PLATFORMS_DOTLIST_README diff --git a/developer/src/kmc-generate/src/template/wordlist-lexical-model/model.kpj b/developer/src/kmc-generate/src/template/wordlist-lexical-model/model.kpj new file mode 100644 index 00000000000..aaef818a396 --- /dev/null +++ b/developer/src/kmc-generate/src/template/wordlist-lexical-model/model.kpj @@ -0,0 +1,10 @@ + + + + 2.0 + True + lexicalmodel + True + True + + diff --git a/developer/src/kmc-generate/src/template/wordlist-lexical-model/model.model_info b/developer/src/kmc-generate/src/template/wordlist-lexical-model/model.model_info new file mode 100644 index 00000000000..ff304d45eef --- /dev/null +++ b/developer/src/kmc-generate/src/template/wordlist-lexical-model/model.model_info @@ -0,0 +1,7 @@ +{ + "license": "mit", + "languages": [ + $LANGUAGES_MODEL_INFO + ], + "description": "$DESCRIPTION" +} diff --git a/developer/src/kmc-generate/src/template/wordlist-lexical-model/source/model.kps b/developer/src/kmc-generate/src/template/wordlist-lexical-model/source/model.kps new file mode 100644 index 00000000000..de5f1fd948d --- /dev/null +++ b/developer/src/kmc-generate/src/template/wordlist-lexical-model/source/model.kps @@ -0,0 +1,54 @@ + + + + $KEYMANVERSION + 12.0 + + + + readme.htm + ..\LICENSE.md + + + $NAME + $COPYRIGHT + $AUTHOR + $VERSION + $DESCRIPTION + + + + ..\build\$ID.model.js + + 0 + .js + + + welcome.htm + + 0 + .htm + + + readme.htm + + 0 + .htm + + + ..\LICENSE.md + + 0 + .md + + + + + + $NAME + $ID + $PACKAGE_LANGUAGES + + + + diff --git a/developer/src/kmc-generate/src/template/wordlist-lexical-model/source/model.ts b/developer/src/kmc-generate/src/template/wordlist-lexical-model/source/model.ts new file mode 100644 index 00000000000..529b9df996c --- /dev/null +++ b/developer/src/kmc-generate/src/template/wordlist-lexical-model/source/model.ts @@ -0,0 +1,14 @@ +/* + $NAME $VERSION + + This is a minimal lexical model source that uses a tab delimited wordlist. + See documentation online at https://help.keyman.com/developer/ for + additional parameters. +*/ + +const source: LexicalModelSource = { + format: 'trie-1.0', + wordBreaker: 'default', + sources: ['wordlist.tsv'], +}; +export default source; \ No newline at end of file diff --git a/developer/src/kmc-generate/src/template/wordlist-lexical-model/source/readme.htm b/developer/src/kmc-generate/src/template/wordlist-lexical-model/source/readme.htm new file mode 100644 index 00000000000..041cf910315 --- /dev/null +++ b/developer/src/kmc-generate/src/template/wordlist-lexical-model/source/readme.htm @@ -0,0 +1,24 @@ + + + + + + $NAME + + + + +

$NAME

+ +

+ $DESCRIPTION +

+ +

$COPYRIGHT

+ + + diff --git a/developer/src/kmc-generate/src/template/wordlist-lexical-model/source/welcome.htm b/developer/src/kmc-generate/src/template/wordlist-lexical-model/source/welcome.htm new file mode 100644 index 00000000000..e8e6e7b4f5c --- /dev/null +++ b/developer/src/kmc-generate/src/template/wordlist-lexical-model/source/welcome.htm @@ -0,0 +1,28 @@ + + + + + + Start Using $NAME + + + + +

Start Using $NAME

+ +

+ $DESCRIPTION +

+ +

Wordlist Model Documentation

+ + + +

$COPYRIGHT

+ + + \ No newline at end of file diff --git a/developer/src/kmc-generate/src/template/wordlist-lexical-model/source/wordlist.tsv b/developer/src/kmc-generate/src/template/wordlist-lexical-model/source/wordlist.tsv new file mode 100644 index 00000000000..567a98abc7f --- /dev/null +++ b/developer/src/kmc-generate/src/template/wordlist-lexical-model/source/wordlist.tsv @@ -0,0 +1,16 @@ +# +# $NAME $VERSION generated from template. +# +# This is an example tab-separated wordlist file that can be edited in a spreadsheet +# program or regenerated from a wordlist tool. See lexical model documentation at +# https://help.keyman.com/developer/ for tools. +# +# Columns +# ======= +# WORD FREQUENCY (Optional)NOTES +# +the 100 +example 5 +hello 10 +world 8 +wordlist 3 \ No newline at end of file diff --git a/developer/src/kmc-generate/test/test-messages.ts b/developer/src/kmc-generate/test/test-messages.ts new file mode 100644 index 00000000000..74f5b77778b --- /dev/null +++ b/developer/src/kmc-generate/test/test-messages.ts @@ -0,0 +1,10 @@ +import 'mocha'; +import { GeneratorMessages } from '../src/generator-messages.js'; +import { verifyCompilerMessagesObject } from '@keymanapp/developer-test-helpers'; +import { CompilerErrorNamespace } from '@keymanapp/common-types'; + +describe('GeneratorMessages', function () { + it('should have a valid GeneratorMessages object', function() { + return verifyCompilerMessagesObject(GeneratorMessages, CompilerErrorNamespace.Generator); + }); +}); diff --git a/developer/src/kmc-generate/test/tsconfig.json b/developer/src/kmc-generate/test/tsconfig.json new file mode 100644 index 00000000000..f281f40353e --- /dev/null +++ b/developer/src/kmc-generate/test/tsconfig.json @@ -0,0 +1,27 @@ +{ + "extends": "../../kmc/tsconfig.kmc-base.json", + + "compilerOptions": { + "rootDir": ".", + "rootDirs": ["./", "../src/"], + "outDir": "../build/test", + "baseUrl": ".", + "strictNullChecks": true, + "paths": { + // "@keymanapp/keyman-version": ["../../../common/web/keyman-version/keyman-version.mts"], + "@keymanapp/common-types": ["../../../../common/web/types/src/main"], + "@keymanapp/developer-test-helpers": ["../../common/web/test-helpers/index"], + // "@keymanapp/": ["core/include/ldml/ldml-keyboard-constants"], + }, + }, + "include": [ + "**/test-*.ts", + "./helpers/index.ts" + ], + "references": [ + { "path": "../../../../common/web/keyman-version" }, + { "path": "../../../../common/web/types/" }, + { "path": "../../common/web/test-helpers/" }, + { "path": "../" } + ] +} \ No newline at end of file diff --git a/developer/src/kmc-generate/tsconfig.json b/developer/src/kmc-generate/tsconfig.json new file mode 100644 index 00000000000..1fc0a249be4 --- /dev/null +++ b/developer/src/kmc-generate/tsconfig.json @@ -0,0 +1,25 @@ +{ + "extends": "../../../tsconfig.base.json", + + "compilerOptions": { + "outDir": "build/src/", + "rootDir": "src/", + "baseUrl": ".", + + "paths": { + "@keymanapp/keyman-version": ["../../../common/web/keyman-version/keyman-version.mts"], + "@keymanapp/common-types": ["../../../common/web/types/src/main"], + }, + + }, + "include": [ + "src/**/*.ts", + ], + "exclude": [ + "src/template/**/*.ts", + ], + "references": [ + { "path": "../../../common/web/keyman-version" }, + { "path": "../../../common/web/types/" }, + ] +} diff --git a/developer/src/kmc/build.sh b/developer/src/kmc/build.sh index a6bc22f2622..b6cb86337f3 100755 --- a/developer/src/kmc/build.sh +++ b/developer/src/kmc/build.sh @@ -22,6 +22,7 @@ builder_describe "Build Keyman Keyboard Compiler kmc" \ "@/common/web/types" \ "@/developer/src/common/web/utils" \ "@/developer/src/kmc-analyze" \ + "@/developer/src/kmc-generate" \ "@/developer/src/kmc-keyboard-info" \ "@/developer/src/kmc-kmn" \ "@/developer/src/kmc-ldml" \ @@ -92,6 +93,7 @@ readonly PACKAGES=( core/include/ldml developer/src/common/web/utils developer/src/kmc-analyze + developer/src/kmc-generate developer/src/kmc-keyboard-info developer/src/kmc-kmn developer/src/kmc-ldml diff --git a/developer/src/kmc/package.json b/developer/src/kmc/package.json index 23def4adb07..3162e725628 100644 --- a/developer/src/kmc/package.json +++ b/developer/src/kmc/package.json @@ -38,6 +38,7 @@ "@keymanapp/developer-utils": "*", "@keymanapp/keyman-version": "*", "@keymanapp/kmc-analyze": "*", + "@keymanapp/kmc-generate": "*", "@keymanapp/kmc-keyboard-info": "*", "@keymanapp/kmc-kmn": "*", "@keymanapp/kmc-ldml": "*", diff --git a/developer/src/kmc/src/commands/generate.ts b/developer/src/kmc/src/commands/generate.ts new file mode 100644 index 00000000000..052815c5f39 --- /dev/null +++ b/developer/src/kmc/src/commands/generate.ts @@ -0,0 +1,122 @@ +// import * as path from 'path'; +import { Command } from 'commander'; +import { NodeCompilerCallbacks } from '../util/NodeCompilerCallbacks.js'; +import { InfrastructureMessages } from '../messages/infrastructureMessages.js'; +import { BaseOptions } from '../util/baseOptions.js'; +import { exitProcess } from '../util/sysexits.js'; +import { AbstractGenerator, GeneratorOptions, KeymanKeyboardGenerator, LdmlKeyboardGenerator, LexicalModelGenerator } from '@keymanapp/kmc-generate'; + + +export function declareGenerate(program: Command) { + const command = program.command('generate'); + declareGenerateKmnKeyboard(command); + declareGenerateLdmlKeyboard(command); + declareGenerateLexicalModel(command); +} + +function declareGenerateKmnKeyboard(command: Command) { + const subCommand = command.command('keyman-keyboard '); + BaseOptions.addLogLevel(subCommand); + subCommand + .description('Generate a .kmn keyboard project') + .option('--targets, -t ', 'Target platforms', ['any']) + .option('--out-path, -o ', 'Output path (may exist)') + .option('--name, -n ', 'Keyboard descriptive name') + .option('--copyright, -c ', 'Copyright holder') /* © yyyy */ + .option('--version, -v ', 'Keyboard version', '1.0') + .option('--language-tags, -l ', 'BCP-47 language tags') + .option('--author, -a ', 'Name of keyboard author') + .option('--icon', 'Include a generated icon', true) + .option('--description', 'Short description of the project, Markdown') + .action(generateKmnKeyboard); +} + +function declareGenerateLdmlKeyboard(command: Command) { + const ldmlSubCommand = command.command('ldml-keyboard '); + BaseOptions.addLogLevel(ldmlSubCommand); + ldmlSubCommand + .description('Generate an LDML .xml keyboard project') + .option('--out-path, -o ', 'Output path (may exist)') + .option('--name, -n ', 'Keyboard descriptive name') + .option('--copyright, -c ', 'Copyright holder') /* © yyyy */ + .option('--version, -v ', 'Keyboard version', '1.0') + .option('--language-tags, -l ', 'BCP-47 language tags') + .option('--author, -a ', 'Name of keyboard author') + .option('--description', 'Short description of the project, Markdown') + .action(generateLdmlKeyboard); +} + +function declareGenerateLexicalModel(command: Command) { + const modelSubCommand = command.command('lexical-model '); + BaseOptions.addLogLevel(modelSubCommand); + modelSubCommand + .description('Generate a wordlist lexical model project') + .option('--out-path, -o ', 'Output path (may exist)') + .option('--name, -n ', 'Keyboard descriptive name') + .option('--copyright, -c ', 'Copyright holder') /* © yyyy */ + .option('--version, -v ', 'Keyboard version', '1.0') + .option('--language-tags, -l ', 'BCP-47 language tags') + .option('--author, -a ', 'Name of keyboard author') + .option('--description', 'Short description of the project, Markdown') + .action(generateLexicalModel); + + //TODO:required opts +} + +function commanderOptionsToGeneratorOptions(id: string, options: any): GeneratorOptions { + const result: GeneratorOptions = { + icon: options.icon ?? true, + languageTags: options.languageTags ?? [], + name: options.name ?? id, + outPath: options.outPath ?? '.', + targets: options.targets ?? ['any'], + version: options.version ?? '1.0', + author: options.author ?? id, + copyright: options.copyright ?? options.author ?? id, + logLevel: options.logLevel ?? 'info', + description: options.description ?? '', + }; + return result; +} + +const generateKmnKeyboard = async (ids: string[], _options: any, commander: any) => + generate(KeymanKeyboardGenerator, ids, commander); +const generateLdmlKeyboard = async (ids: string[], _options: any, commander: any) => + generate(LdmlKeyboardGenerator, ids, commander); +const generateLexicalModel = async (ids: string[], _options: any, commander: any) => + generate(LexicalModelGenerator, ids, commander); + +async function generate( + generatorClass: typeof AbstractGenerator, + ids: string | string[], + commander: any +): Promise { + if(typeof ids != 'string') { + //TODO vvv + console.error('only 1 id may be specified'); + return await exitProcess(1); + } + + const id = ids; + const commanderOptions = commander.optsWithGlobals(); + const options = commanderOptionsToGeneratorOptions(id, commanderOptions); + if(!id) { + //TODO vvv + console.error('id must be specified'); + return await exitProcess(1); + } + + let callbacks = new NodeCompilerCallbacks({logLevel: options.logLevel}); + try { + const generator = new generatorClass(callbacks, options); + if(!await generator.init(id)) { + return await exitProcess(1); + } + if(!await generator.run()) { + return await exitProcess(1); + } + } catch(e) { + callbacks.reportMessage(InfrastructureMessages.Fatal_UnexpectedException({e})); + return await exitProcess(1); + } +} diff --git a/developer/src/kmc/src/kmc.ts b/developer/src/kmc/src/kmc.ts index 92b25c765b3..328fe1ce4e2 100644 --- a/developer/src/kmc/src/kmc.ts +++ b/developer/src/kmc/src/kmc.ts @@ -10,6 +10,7 @@ import { KeymanSentry, loadOptions } from '@keymanapp/developer-utils'; import KEYMAN_VERSION from "@keymanapp/keyman-version"; import { TestKeymanSentry } from './util/TestKeymanSentry.js'; import { exitProcess } from './util/sysexits.js'; +import { declareGenerate } from './commands/generate.js'; await TestKeymanSentry.runTestIfCLRequested(); if(KeymanSentry.isEnabled()) { @@ -47,6 +48,7 @@ async function run() { declareBuild(program); declareAnalyze(program); + declareGenerate(program); /* Future commands: declareClean(program); diff --git a/developer/src/kmc/src/messages/messageNamespaces.ts b/developer/src/kmc/src/messages/messageNamespaces.ts index a99c98504cf..23c12c63598 100644 --- a/developer/src/kmc/src/messages/messageNamespaces.ts +++ b/developer/src/kmc/src/messages/messageNamespaces.ts @@ -1,5 +1,6 @@ import { CommonTypesMessages, CompilerErrorNamespace } from '@keymanapp/common-types'; import { AnalyzerMessages } from '@keymanapp/kmc-analyze'; +import { GeneratorMessages } from '@keymanapp/kmc-generate'; import { KeyboardInfoCompilerMessages } from '@keymanapp/kmc-keyboard-info'; import { KmnCompilerMessages, KmwCompilerMessages } from '@keymanapp/kmc-kmn'; import { LdmlKeyboardCompilerMessages } from '@keymanapp/kmc-ldml'; @@ -20,6 +21,7 @@ export const messageNamespaces: Record = { [CompilerErrorNamespace.KmwCompiler]: KmwCompilerMessages, [CompilerErrorNamespace.ModelInfoCompiler]: ModelInfoCompilerMessages, [CompilerErrorNamespace.KeyboardInfoCompiler]: KeyboardInfoCompilerMessages, + [CompilerErrorNamespace.Generator]: GeneratorMessages, }; // This works around pain points in enumerating enum members in Typescript diff --git a/developer/src/kmc/tsconfig.json b/developer/src/kmc/tsconfig.json index 0ba354075d5..157c84bf203 100644 --- a/developer/src/kmc/tsconfig.json +++ b/developer/src/kmc/tsconfig.json @@ -8,6 +8,7 @@ "paths": { "@keymanapp/common-types": [ "../../../common/web/types/src/main" ], "@keymanapp/kmc-analyze": [ "../kmc-analyze/src/index" ], + "@keymanapp/kmc-generate": [ "../kmc-generate/src/main" ], "@keymanapp/kmc-keyboard-info": [ "../kmc-keyboard-info/src/index" ], "@keymanapp/kmc-kmn": [ "../kmc-kmn/src/main" ], "@keymanapp/kmc-ldml": [ "../kmc-ldml/src/main" ], @@ -17,12 +18,13 @@ } }, "include": [ - "src/**/*.ts" + "src/**/*.ts", ], "references": [ { "path": "../../../common/web/keyman-version" }, { "path": "../../../common/web/types" }, { "path": "../kmc-analyze" }, + { "path": "../kmc-generate" }, { "path": "../kmc-keyboard-info" }, { "path": "../kmc-kmn" }, { "path": "../kmc-ldml" }, diff --git a/developer/src/test/auto/Makefile b/developer/src/test/auto/Makefile index bb5e826f817..ed53c8cc2c7 100644 --- a/developer/src/test/auto/Makefile +++ b/developer/src/test/auto/Makefile @@ -23,6 +23,7 @@ developer-tests: \ package-info \ kmc \ kmc-analyze \ + kmc-generate \ kmc-keyboard-info \ kmc-kmn \ kmc-ldml \ @@ -74,6 +75,10 @@ kmc-analyze: .virtual cd $(DEVELOPER_ROOT)\src\kmc-analyze $(MAKE) $(TARGET) +kmc-generate: .virtual + cd $(DEVELOPER_ROOT)\src\kmc-generate + $(MAKE) $(TARGET) + kmc-keyboard-info: .virtual cd $(DEVELOPER_ROOT)\src\kmc-keyboard-info $(MAKE) $(TARGET) diff --git a/developer/src/tools/sentry-upload-difs.sh b/developer/src/tools/sentry-upload-difs.sh index 5dd22cc886a..b187d80efbc 100755 --- a/developer/src/tools/sentry-upload-difs.sh +++ b/developer/src/tools/sentry-upload-difs.sh @@ -40,6 +40,7 @@ sourcemap_paths=( ../bin/server ./kmc/build ./kmc-analyze/build + ./kmc-generate/build ./kmc-keyboard-info/build ./kmc-kmn/build ./kmc-ldml/build diff --git a/package-lock.json b/package-lock.json index 7ea71890287..7ee9d966d7e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "developer/src/common/web/test-helpers", "developer/src/common/web/utils", "developer/src/kmc-analyze", + "developer/src/kmc-generate", "developer/src/kmc-keyboard-info", "developer/src/kmc-kmn", "developer/src/kmc-ldml", @@ -975,6 +976,7 @@ "@keymanapp/developer-utils": "*", "@keymanapp/keyman-version": "*", "@keymanapp/kmc-analyze": "*", + "@keymanapp/kmc-generate": "*", "@keymanapp/kmc-keyboard-info": "*", "@keymanapp/kmc-kmn": "*", "@keymanapp/kmc-ldml": "*", @@ -1235,6 +1237,240 @@ "node": ">=0.3.1" } }, + "developer/src/kmc-generate": { + "license": "MIT", + "dependencies": { + "@keymanapp/common-types": "*", + "@keymanapp/keyman-version": "*" + }, + "devDependencies": { + "@keymanapp/developer-test-helpers": "*", + "@keymanapp/resources-gosh": "*", + "@types/chai": "^4.1.7", + "@types/mocha": "^5.2.7", + "@types/node": "^20.4.1", + "@types/semver": "^7.3.12", + "c8": "^7.12.0", + "chai": "^4.3.4", + "chalk": "^2.4.2", + "mocha": "^8.4.0", + "ts-node": "^9.1.1", + "typescript": "^4.9.5" + } + }, + "developer/src/kmc-generate/node_modules/@types/mocha": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", + "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", + "dev": true + }, + "developer/src/kmc-generate/node_modules/@types/node": { + "version": "20.11.28", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.28.tgz", + "integrity": "sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "developer/src/kmc-generate/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "developer/src/kmc-generate/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "developer/src/kmc-generate/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "developer/src/kmc-generate/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "developer/src/kmc-generate/node_modules/js-yaml": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "developer/src/kmc-generate/node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "developer/src/kmc-generate/node_modules/mocha": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", + "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", + "dev": true, + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.1", + "debug": "4.3.1", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.0.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.20", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.1.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 10.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "developer/src/kmc-generate/node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "developer/src/kmc-generate/node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "developer/src/kmc-generate/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "developer/src/kmc-generate/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "developer/src/kmc-generate/node_modules/supports-color/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "developer/src/kmc-generate/node_modules/ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "dev": true, + "dependencies": { + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "typescript": ">=2.7" + } + }, + "developer/src/kmc-generate/node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "developer/src/kmc-keyboard": { "name": "@keymanapp/kmc-keyboard", "extraneous": true, @@ -3206,6 +3442,10 @@ "resolved": "developer/src/kmc-analyze", "link": true }, + "node_modules/@keymanapp/kmc-generate": { + "resolved": "developer/src/kmc-generate", + "link": true + }, "node_modules/@keymanapp/kmc-keyboard-info": { "resolved": "developer/src/kmc-keyboard-info", "link": true diff --git a/package.json b/package.json index d7490161434..719bf977026 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "developer/src/common/web/test-helpers", "developer/src/common/web/utils", "developer/src/kmc-analyze", + "developer/src/kmc-generate", "developer/src/kmc-keyboard-info", "developer/src/kmc-kmn", "developer/src/kmc-ldml", diff --git a/tsconfig.json b/tsconfig.json index 3d6c66b3b4c..9dc4b64edd4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -28,6 +28,8 @@ { "path": "./developer/src/kmc/test/tsconfig.json" }, { "path": "./developer/src/kmc-analyze/tsconfig.json" }, // { "path": "./developer/src/kmc-analyze/test/tsconfig.json" }, + { "path": "./developer/src/kmc-generate/tsconfig.json" }, + { "path": "./developer/src/kmc-generate/test/tsconfig.json" }, { "path": "./developer/src/kmc-kmn/test/tsconfig.json" }, { "path": "./developer/src/kmc-kmn/tsconfig.json" }, { "path": "./developer/src/kmc-keyboard-info/test/tsconfig.json" },