Merge pull request #16135 from keymanapp/chore/developer/14838-consolidate-api-extractor

chore(developer): consolidate api-extractor usage in Developer
This commit is contained in:
Marc Durdin 2026-07-09 18:50:27 +10:00 committed by GitHub
commit 528876b42f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 193 additions and 139 deletions

4
.gitignore vendored
View file

@ -186,3 +186,7 @@ lcov.info
# flag file for build script
.configured
# see common/tools/api-extractor/README.md
tsdoc.json
api-extractor.json

View file

@ -0,0 +1,41 @@
# Introduction
These files are used by `typescript_run_api_extractor()` in typescript.inc.sh.
This is setup only for Developer projects at this time as outputs go into
developer/docs and developer/build; future generalization requires changing only
`$report_temp` and `$report_folder` parameters in
`typescript_run_api_extractor()`.
## api-extractor.template.json
* Reference: https://api-extractor.com
api-extractor.template.json contains a template for api-extractor; these
parameters cannot be passed in to the tool, so we modify this template as needed
with the following parameters:
* `$keyman_root`: the `$KEYMAN_ROOT` variable, with backslash \ translated to
forward slash /
* `$index_d_ts`: the filename `index.d.ts` or the corresponding filename for the
entry point of the project
* `$project_path`: the path for the module, relative to the base of the repo
* `$report_temp`: a temporary path for output files for api-extractor
* `$report_folder`: target folder for completed api-extractor API documentation
## tsdoc.template.json
* Reference: https://tsdoc.org/pages/packages/tsdoc-config/
tsdoc.template.json is copied (unmodified) from this folder into tsdoc.json in
project folders (alongside tsconfig.json) before running api-extractor and
removed again afterwards; there is no way to specify an alternate location for
the file.
tsdoc.template.json includes a definition for "@since" which has been proposed
in https://github.com/microsoft/tsdoc/issues/136.
## Notes
See also .github/workflows/api-verification.yml which does API validation of
Core for desktop platforms.

View file

@ -161,7 +161,7 @@
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
* DEFAULT VALUE: "<projectFolder>/etc/"
*/
"reportFolder": "../docs/api/etc/",
// "reportFolder": "../docs/api/etc/",
/**
* Specifies the folder where the temporary report file is written. The file name portion is determined by
@ -176,7 +176,7 @@
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
* DEFAULT VALUE: "<projectFolder>/temp/"
*/
"reportTempFolder": "../build/api/",
// "reportTempFolder": "../build/api/",
/**
* Whether "forgotten exports" should be included in the API report file. Forgotten exports are declarations
@ -206,7 +206,7 @@
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
* DEFAULT VALUE: "<projectFolder>/build/temp/<unscopedPackageName>.api.json"
*/
"apiJsonFilePath": "../build/api/<unscopedPackageName>.api.json",
// "apiJsonFilePath": "../build/api/<unscopedPackageName>.api.json",
/**
* Whether "forgotten exports" should be included in the doc model file. Forgotten exports are declarations

View file

@ -0,0 +1,15 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"extends": "$keyman_root/common/tools/api-extractor/api-extractor.base.json",
"mainEntryPointFilePath": "<projectFolder>/build/src/$index_d_ts",
"docModel": {
"enabled": true,
"projectFolderUrl": "http://github.com/keymanapp/keyman/tree/master/$project_path",
"apiJsonFilePath": "$report_temp/<unscopedPackageName>.api.json"
},
"apiReport": {
"enabled": true,
"reportFolder": "$report_folder/",
"reportTempFolder": "$report_temp/"
}
}

View file

@ -0,0 +1,15 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json",
"extends": ["@microsoft/api-extractor/extends/tsdoc-base.json"],
"tagDefinitions": [
{
"tagName": "@since",
"syntaxKind": "block",
"allowMultiple": false
}
],
"supportForTags": {
"@since": true
}
}

View file

@ -214,7 +214,7 @@ export interface CopierOptions extends CompilerBaseOptions {
relocateExternalFiles?: boolean;
}
// @public (undocumented)
// @public
export class KeymanProjectCopier implements KeymanCompiler {
// Warning: (ae-forgotten-export) The symbol "CopierAsyncCallbacks" needs to be exported by the entry point main.d.ts
//

View file

@ -933,21 +933,21 @@ declare namespace Osk {
}
export { Osk }
// @public (undocumented)
// @public
function parseMapping(mapping: any): PuaMap;
// @public (undocumented)
// @public
type PuaMap = {
[index: string]: string;
};
// @public (undocumented)
// @public
function remapTouchLayout(source: TouchLayout.TouchLayoutFile, map: PuaMap): boolean;
// @public (undocumented)
// @public
function remapVisualKeyboard(vk: VisualKeyboard.VisualKeyboard, map: PuaMap): boolean;
// @public (undocumented)
// @public
interface StringRef {
// (undocumented)
str: string;
@ -955,7 +955,7 @@ interface StringRef {
usages: StringRefUsage[];
}
// @public (undocumented)
// @public
interface StringRefUsage {
// (undocumented)
count: number;

View file

@ -27,5 +27,5 @@ builder_parse "$@"
builder_run_action clean rm -rf ./build/
builder_run_action configure node_select_version_and_npm_ci
builder_run_action build tsc --build
builder_run_action api api-extractor run --local --verbose
builder_run_action api typescript_run_api_extractor developer/src/kmc-analyze index.d.ts
builder_run_action test typescript_run_eslint_mocha_tests 75

View file

@ -1,12 +0,0 @@
/**
* Config file for API Extractor. For more info, please visit: https://api-extractor.com
*/
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"extends": "../../../config/api-extractor.base.json",
"mainEntryPointFilePath": "<projectFolder>/build/src/index.d.ts",
"docModel": {
"enabled": true,
"projectFolderUrl": "http://github.com/keymanapp/keyman/tree/master/developer/src/kmc-analyze"
}
}

View file

@ -304,7 +304,7 @@ export class AnalyzeOskCharacterUse {
*
* - .json: returns the final aggregated data as an array of strings, which
* can be joined to form a JSON blob of an object with a single member,
* `map`, which is an array of {@link Osk.StringResult} objects.
* `map`, which is an array of {@link @keymanapp/kmc-kmn#Osk.StringResult} objects.
*
* @param format - file format to return - can be '.txt', '.md', or '.json'
* @returns an array of strings, formatted according to the `format`
@ -324,7 +324,7 @@ export class AnalyzeOskCharacterUse {
/**
* Load a JSON-format result file to merge from
* @param filename
* @param filename - the full path to the JSON result file to load
* @returns
*/
private loadPreviousMap(filename: string): Osk.StringResult[] {

View file

@ -33,7 +33,7 @@ builder_parse "$@"
builder_run_action clean rm -rf ./build/
builder_run_action configure node_select_version_and_npm_ci
builder_run_action build tsc --build
builder_run_action api api-extractor run --local --verbose
builder_run_action api typescript_run_api_extractor developer/src/kmc-copy main.d.ts
# note: `export TEST_SAVE_ARTIFACTS=1` to save a copy of artifacts to temp path
# note: `export TEST_SAVE_FIXTURES=1` to get a copy of cloud-based fixtures saved to online/

View file

@ -1,12 +0,0 @@
/**
* Config file for API Extractor. For more info, please visit: https://api-extractor.com
*/
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"extends": "../../../config/api-extractor.base.json",
"mainEntryPointFilePath": "<projectFolder>/build/src/main.d.ts",
"docModel": {
"enabled": true,
"projectFolderUrl": "http://github.com/keymanapp/keyman/tree/master/developer/src/kmc-copy"
}
}

View file

@ -81,6 +81,10 @@ export interface CopierResult extends KeymanCompilerResult {
artifacts: CopierArtifacts;
};
/**
* @public
* Copy a project and rename internal references
*/
export class KeymanProjectCopier implements KeymanCompiler {
options: CopierOptions;
callbacks: CompilerCallbacks;
@ -111,8 +115,8 @@ export class KeymanProjectCopier implements KeymanCompiler {
* artifacts on success. The files are passed in by name, and the compiler
* will use callbacks as passed to the {@link KeymanProjectCopier.init}
* function to read any input files by disk.
* @param source Source file or folder to copy. Can be a local file or folder, https://github.com/.../repo[/path], or cloud:id
* @returns Binary artifacts on success, null on failure.
* @param source - Source file or folder to copy. Can be a local file or folder, https://github.com/.../repo[/path], or cloud:id
* @returns Binary artifacts on success, null on failure.
*/
public async run(source: string): Promise<CopierResult> {
@ -174,7 +178,7 @@ export class KeymanProjectCopier implements KeymanCompiler {
/**
* Resolve the source project file to either a local filesystem file,
* or a reference on GitHub
* @param source
* @param source - URI to a project file
* @returns path to .kpj (either local or remote)
*/
private async getSourceProject(source: string): Promise<string | GitHubRef> {
@ -196,7 +200,7 @@ export class KeymanProjectCopier implements KeymanCompiler {
/**
* Resolve source path to the contained project file; the project
* file must have the same basename as the folder in this case
* @param source
* @param source - local file path to a .kpj project file
* @returns
*/
private getLocalFolderProject(source: string): string {
@ -212,7 +216,7 @@ export class KeymanProjectCopier implements KeymanCompiler {
/**
* Resolve source path to the input .kpj filename, folder name
* is not relevant when .kpj filename is passed in
* @param source
* @param source - local file path to a .kpj project file
* @returns
*/
private getLocalFileProject(source: string): string {
@ -224,7 +228,7 @@ export class KeymanProjectCopier implements KeymanCompiler {
* `[https://]github.com/owner/repo/branch/path/to/kpj`
* The path must be fully qualified, referencing the .kpj file; it
* cannot just be the folder where the .kpj is found
* @param source
* @param source - URL to a .kpj project file on GitHub
* @returns a promise: GitHub reference to the source for the keyboard, or null on failure
*/
private async getGitHubSourceProject(source: string): Promise<GitHubRef> {
@ -273,7 +277,7 @@ export class KeymanProjectCopier implements KeymanCompiler {
* The `keyboard_id` parameter should be a valid id (a-z0-9_), as found at
* https://keyman.com/keyboards; alternatively if it is a model_id, it should
* have the format author.bcp47.uniq
* @param source
* @param source - a reference to a keyboard or model project on Keyman Cloud
* @returns a promise: GitHub reference to the source for the keyboard, or null on failure
*/
private async getCloudSourceProject(source: string): Promise<GitHubRef> {
@ -613,8 +617,8 @@ export class KeymanProjectCopier implements KeymanCompiler {
/**
* renames matching filename to the output filename pattern, and prepends the
* outputPath
* @param filename
* @param outputPath
* @param filename - input filename to rename
* @param outputPath - target filename path
* @returns
*/
private generateNewFilename(filename: string, outputPath: string): string {

View file

@ -39,5 +39,5 @@ do_build() {
builder_run_action clean rm -rf ./build/ ./tsconfig.tsbuildinfo
builder_run_action configure node_select_version_and_npm_ci
builder_run_action build do_build
builder_run_action api api-extractor run --local --verbose
builder_run_action api typescript_run_api_extractor developer/src/kmc-generate main.d.ts
builder_run_action test typescript_run_eslint_mocha_tests

View file

@ -1,12 +0,0 @@
/**
* Config file for API Extractor. For more info, please visit: https://api-extractor.com
*/
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"extends": "../../../config/api-extractor.base.json",
"mainEntryPointFilePath": "<projectFolder>/build/src/main.d.ts",
"docModel": {
"enabled": true,
"projectFolderUrl": "http://github.com/keymanapp/keyman/tree/master/developer/src/kmc-generate"
}
}

View file

@ -32,7 +32,7 @@ builder_parse "$@"
builder_run_action clean rm -rf ./build/ ./tsconfig.tsbuildinfo
builder_run_action configure node_select_version_and_npm_ci
builder_run_action build tsc --build
builder_run_action api api-extractor run --local --verbose
builder_run_action api typescript_run_api_extractor developer/src/kmc-keyboard-info index.d.ts
builder_run_action test typescript_run_eslint_mocha_tests
#-------------------------------------------------------------------------------------------------------------------

View file

@ -1,12 +0,0 @@
/**
* Config file for API Extractor. For more info, please visit: https://api-extractor.com
*/
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"extends": "../../../config/api-extractor.base.json",
"mainEntryPointFilePath": "<projectFolder>/build/src/index.d.ts",
"docModel": {
"enabled": true,
"projectFolderUrl": "http://github.com/keymanapp/keyman/tree/master/developer/src/kmc-keyboard-info"
}
}

View file

@ -65,5 +65,5 @@ function do_test() {
}
builder_run_action build do_build
builder_run_action api api-extractor run --local --verbose
builder_run_action api typescript_run_api_extractor developer/src/kmc-kmn main.d.ts
builder_run_action test do_test

View file

@ -1,12 +0,0 @@
/**
* Config file for API Extractor. For more info, please visit: https://api-extractor.com
*/
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"extends": "../../../config/api-extractor.base.json",
"mainEntryPointFilePath": "<projectFolder>/build/src/main.d.ts",
"docModel": {
"enabled": true,
"projectFolderUrl": "http://github.com/keymanapp/keyman/tree/master/developer/src/kmc-kmn"
}
}

View file

@ -2,17 +2,27 @@ import { TouchLayout } from "@keymanapp/common-types";
import { VisualKeyboard } from "@keymanapp/common-types";
import { SchemaValidators } from "@keymanapp/common-types";
/**
* @public
* Records the number of references to an OSK key cap string for a specific
* file
*/
export interface StringRefUsage {
filename: string;
count: number;
};
/**
* @public
* Tracks usage of a single OSK key cap string across multiple files
*/
export interface StringRef {
str: string;
usages: StringRefUsage[];
};
/**
* @public
* Represents a single key cap found by `AnalyzeOskCharacterUse`
*/
export interface StringResult {
@ -23,13 +33,23 @@ export interface StringResult {
/** hexadecimal single character in PUA range, without 'U+' prefix, e.g. 'F100' */
pua: string;
/** files in which the string is referenced; will be an array of
* {@link StringRefUsage} if includeCounts is true, otherwise will be an array
* {@link @keymanapp/kmc-kmn#Osk.StringRefUsage} if includeCounts is true, otherwise will be an array
* of strings listing files in which the key cap may be found */
usages: StringRefUsage[] | string[];
};
/**
* @public
* Maps a source OSK key cap string to a PUA character
*/
export type PuaMap = {[index:string]: string};
/**
* @public
* Parse a map object loaded from a displaymap file into a PuaMap
* @param mapping - source object to parse, must be in displayMap JSON format
* @returns
*/
export function parseMapping(mapping: any) {
if(!SchemaValidators.default.displayMap(<any>mapping))
/* c8 ignore next 3 */
@ -60,6 +80,13 @@ function remap(text: string, map: PuaMap) {
return text;
}
/**
* @public
* Remap key caps in the `vk` visual keyboard object to use PUA characters from `map`
* @param vk - source visual keyboard object to remap, updated in place
* @param map - PUA string mapping to apply
* @returns
*/
export function remapVisualKeyboard(vk: VisualKeyboard.VisualKeyboard, map: PuaMap): boolean {
let dirty = false;
for(const key of vk.keys) {
@ -73,6 +100,13 @@ export function remapVisualKeyboard(vk: VisualKeyboard.VisualKeyboard, map: PuaM
return dirty;
}
/**
* @public
* Remap key caps in the `source` touch layout object to use PUA characters from `map`
* @param source - source touch layout object to remap, updated in place
* @param map - PUA string mapping to apply
* @returns
*/
export function remapTouchLayout(source: TouchLayout.TouchLayoutFile, map: PuaMap) {
let dirty = false;
const scanKey = (key: TouchLayout.TouchLayoutKey | TouchLayout.TouchLayoutSubKey) => {

View file

@ -85,5 +85,5 @@ builder_run_action clean do_clean
builder_run_action configure do_configure
builder_run_action build do_build
builder_run_action build-fixtures do_build_fixtures
builder_run_action api api-extractor run --local --verbose
builder_run_action api typescript_run_api_extractor developer/src/kmc-ldml main.d.ts
builder_run_action test typescript_run_eslint_mocha_tests 90

View file

@ -1,12 +0,0 @@
/**
* Config file for API Extractor. For more info, please visit: https://api-extractor.com
*/
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"extends": "../../../config/api-extractor.base.json",
"mainEntryPointFilePath": "<projectFolder>/build/src/main.d.ts",
"docModel": {
"enabled": true,
"projectFolderUrl": "http://github.com/keymanapp/keyman/tree/master/developer/src/kmc-ldml"
}
}

View file

@ -29,5 +29,5 @@ builder_parse "$@"
builder_run_action clean rm -rf ./build/ ./tsconfig.tsbuildinfo
builder_run_action configure node_select_version_and_npm_ci
builder_run_action build tsc --build
builder_run_action api api-extractor run --local --verbose
builder_run_action api typescript_run_api_extractor developer/src/kmc-model-info index.d.ts
builder_run_action test typescript_run_eslint_mocha_tests 55

View file

@ -1,12 +0,0 @@
/**
* Config file for API Extractor. For more info, please visit: https://api-extractor.com
*/
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"extends": "../../../config/api-extractor.base.json",
"mainEntryPointFilePath": "<projectFolder>/build/src/index.d.ts",
"docModel": {
"enabled": true,
"projectFolderUrl": "http://github.com/keymanapp/keyman/tree/master/developer/src/kmc-model-info"
}
}

View file

@ -36,5 +36,5 @@ function do_build() {
builder_run_action clean rm -rf ./build/ ./tsconfig.tsbuildinfo
builder_run_action configure node_select_version_and_npm_ci
builder_run_action build do_build
builder_run_action api api-extractor run --local --verbose
builder_run_action api typescript_run_api_extractor developer/src/kmc-model main.d.ts
builder_run_action test typescript_run_eslint_mocha_tests

View file

@ -1,12 +0,0 @@
/**
* Config file for API Extractor. For more info, please visit: https://api-extractor.com
*/
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"extends": "../../../config/api-extractor.base.json",
"mainEntryPointFilePath": "<projectFolder>/build/src/main.d.ts",
"docModel": {
"enabled": true,
"projectFolderUrl": "http://github.com/keymanapp/keyman/tree/master/developer/src/kmc-model"
}
}

View file

@ -34,5 +34,5 @@ builder_parse "$@"
builder_run_action clean rm -rf ./build/ ./tsconfig.tsbuildinfo
builder_run_action configure node_select_version_and_npm_ci
builder_run_action build tsc --build
builder_run_action api api-extractor run --local --verbose
builder_run_action api typescript_run_api_extractor developer/src/kmc-package main.d.ts
builder_run_action test typescript_run_eslint_mocha_tests

View file

@ -1,12 +0,0 @@
/**
* Config file for API Extractor. For more info, please visit: https://api-extractor.com
*/
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"extends": "../../../config/api-extractor.base.json",
"mainEntryPointFilePath": "<projectFolder>/build/src/main.d.ts",
"docModel": {
"enabled": true,
"projectFolderUrl": "http://github.com/keymanapp/keyman/tree/master/developer/src/kmc-package"
}
}

View file

@ -58,3 +58,52 @@ typescript_run_eslint_mocha_tests() {
echo "##teamcity[flowFinished flowId='unit_tests']"
fi
}
#
# Run api-extractor, preparing config files for each folder. As the config files
# are largely identical for each project, we copy them in rather than
# duplicating them across the repo (as that is hard to maintain over time)
#
# NOTE: this is setup only for Developer projects at this time as outputs go
# into developer/docs and developer/build; future generalization requires
# changing only report_temp and report_folder parameters.
#
# See also: /common/tools/api-extractor/README.md
#
typescript_run_api_extractor() {
project_path="$1"
index_d_ts="$2"
# tsdoc config file must be in same folder as tsconfig.json
cp "${KEYMAN_ROOT}/common/tools/api-extractor/tsdoc.template.json" "${THIS_SCRIPT_PATH}/tsdoc.json"
# api-extractor configuration must be stored in a file, so patch the
# file with the relevant parameters
if builder_is_windows; then
# replace \ with / on Windows in KEYMAN_ROOT path, so we don't end up with
# silly unescaped strings in JSON
keyman_root="$(echo "$KEYMAN_ROOT" | sed "s=\\\=/=g")"
else
keyman_root="${KEYMAN_ROOT}"
fi
# For now, these two variables are Developer-specific
report_temp="$keyman_root/developer/build/api"
report_folder="$keyman_root/developer/docs/api/etc"
export project_path index_d_ts keyman_root report_temp report_folder
envsubst "\$keyman_root,\$project_path,\$index_d_ts,\$report_temp,\$report_folder" \
< "${KEYMAN_ROOT}/common/tools/api-extractor/api-extractor.template.json" \
> "${THIS_SCRIPT_PATH}/api-extractor.json"
export -n project_path index_d_ts keyman_root report_temp report_folder
api-extractor run \
--local \
--verbose \
--config "${THIS_SCRIPT_PATH}/api-extractor.json"
rm "${THIS_SCRIPT_PATH}/tsdoc.json"
rm "${THIS_SCRIPT_PATH}/api-extractor.json"
}