Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

change(web): remove support for es5 🏗️ #11881

Merged
merged 15 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions android/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ KMEA/**/assets/keymanandroid.js
KMEA/**/assets/keyman.js.map
KMEA/**/assets/keymanweb-webview.js
KMEA/**/assets/keymanweb-webview.js.map
KMEA/**/assets/keymanweb-webview.es5.js
KMEA/**/assets/keymanweb-webview.es5.js.map
KMEA/**/assets/map-polyfill.js
KMEA/**/assets/sentry.min.js
KMEA/**/assets/keyman-sentry.js
Expand Down
30 changes: 0 additions & 30 deletions android/KMEA/app/src/main/assets/keyboard.es5.html

This file was deleted.

32 changes: 5 additions & 27 deletions android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -295,9 +295,7 @@ public String toString() {

// Keyman files
protected static final String KMFilename_KeyboardHtml = "keyboard.html";
protected static final String KMFilename_KeyboardHtml_Legacy = "keyboard.es5.html";
protected static final String KMFilename_JSEngine = "keymanweb-webview.js";
protected static final String KMFilename_JSLegacyEngine = "keymanweb-webview.es5.js";
protected static final String KMFilename_JSSentry = "sentry.min.js";
protected static final String KMFilename_JSSentryInit = "keyman-sentry.js";
protected static final String KMFilename_AndroidHost = "android-host.js";
Expand Down Expand Up @@ -858,25 +856,11 @@ public static boolean copyHTMLBannerAssets(Context context, String path) {
private static void copyAssets(Context context) {
AssetManager assetManager = context.getAssets();

// Will build a temp WebView in order to check Chrome version internally.
boolean legacyMode = WebViewUtils.getEngineWebViewVersionStatus(context, null, null) != WebViewUtils.EngineWebViewVersionStatus.FULL;

try {
// Copy KMW files
if(legacyMode) {
// Replaces the standard ES6-friendly version of the host page with a legacy one that
// includes polyfill requests and that links the legacy, ES5-compatible version of KMW.
copyAssetWithRename(context, KMFilename_KeyboardHtml_Legacy, KMFilename_KeyboardHtml, "", true);

copyAsset(context, KMFilename_JSLegacyEngine, "", true);
} else {
copyAsset(context, KMFilename_KeyboardHtml, "", true);

// For versions of Chrome with full ES6 support, we use the ES6 artifact.
copyAsset(context, KMFilename_JSEngine, "", true);
}
copyAsset(context, KMFilename_KeyboardHtml, "", true);

// Is still built targeting ES5.
copyAsset(context, KMFilename_JSEngine, "", true);
copyAsset(context, KMFilename_JSSentry, "", true);
copyAsset(context, KMFilename_JSSentryInit, "", true);
copyAsset(context, KMFilename_AndroidHost, "", true);
Expand All @@ -887,12 +871,6 @@ private static void copyAssets(Context context) {
// Copy default keyboard font
copyAsset(context, KMDefault_KeyboardFont, "", true);

if(legacyMode) {
copyAsset(context, KMFilename_JSPolyfill, "", true);
copyAsset(context, KMFilename_JSPolyfill2, "", true);
copyAsset(context, KMFilename_JSPolyfill3, "", true);
}

// Keyboard packages directory
File packagesDir = new File(getPackagesDir());
if (!packagesDir.exists()) {
Expand Down Expand Up @@ -1638,7 +1616,7 @@ public static boolean removeKeyboard(Context context, int position) {

public static boolean isDefaultKey(String key) {
return (
key != null &&
key != null &&
key.equals(KMString.format("%s_%s", KMDefault_LanguageID, KMDefault_KeyboardID)));
}

Expand Down Expand Up @@ -2084,11 +2062,11 @@ public static Point getWindowSize(Context context) {
wm.getDefaultDisplay().getSize(size);
return size;
}

WindowMetrics windowMetrics = wm.getCurrentWindowMetrics();
return new Point(
windowMetrics.getBounds().width(),
windowMetrics.getBounds().height());
windowMetrics.getBounds().height());
}

public static float getWindowDensity(Context context) {
Expand Down
2 changes: 0 additions & 2 deletions android/KMEA/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,6 @@ if builder_start_action build:engine; then
echo "Copying Keyman Web artifacts"
cp "$KEYMAN_WEB_ROOT/build/app/webview/$CONFIG/keymanweb-webview.js" "$ENGINE_ASSETS/keymanweb-webview.js"
cp "$KEYMAN_WEB_ROOT/build/app/webview/$CONFIG/keymanweb-webview.js.map" "$ENGINE_ASSETS/keymanweb-webview.js.map"
cp "$KEYMAN_WEB_ROOT/build/app/webview/$CONFIG/keymanweb-webview.es5.js" "$ENGINE_ASSETS/keymanweb-webview.es5.js"
cp "$KEYMAN_WEB_ROOT/build/app/webview/$CONFIG/keymanweb-webview.es5.js.map" "$ENGINE_ASSETS/keymanweb-webview.es5.js.map"
cp "$KEYMAN_WEB_ROOT/build/app/webview/$CONFIG/map-polyfill.js" "$ENGINE_ASSETS/map-polyfill.js"
cp "$KEYMAN_WEB_ROOT/build/app/resources/osk/ajax-loader.gif" "$ENGINE_ASSETS/ajax-loader.gif"
cp "$KEYMAN_WEB_ROOT/build/app/resources/osk/kmwosk.css" "$ENGINE_ASSETS/kmwosk.css"
Expand Down
2 changes: 1 addition & 1 deletion common/tools/sourcemap-path-remapper/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"inlineSources": true,
"sourceRoot": "/common/tools/sourcemap-path-remapper/src",
"lib": ["dom", "es6"],
"target": "es5",
"target": "es6",
"types": ["node"],
"downlevelIteration": true,
"baseUrl": "./",
Expand Down
3 changes: 1 addition & 2 deletions common/web/es-bundling/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
################################ Main script ################################

builder_describe "Builds KMW's esbuild-oriented common configuration & tooling" \
"@/common/web/tslib" \
"clean" \
"configure" \
"build"
Expand All @@ -24,4 +23,4 @@ builder_parse "$@"

builder_run_action configure verify_npm_setup
builder_run_action clean rm -rf build/
builder_run_action build tsc -b tsconfig.json
builder_run_action build tsc -b tsconfig.json
4 changes: 2 additions & 2 deletions common/web/es-bundling/src/common-bundle.mts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ let profilePath;
let sourceRoot;
let platform;

let jsVersionTarget='es5';
let jsVersionTarget='es6';

function doHelp(errCode?: number) {
console.log(`
Expand Down Expand Up @@ -141,4 +141,4 @@ const results = await esbuild.build(config);
if(results.metafile) {
let filesizeProfile = await esbuild.analyzeMetafile(results.metafile, { verbose: true });
fs.writeFileSync(profilePath, filesizeProfile);
}
}
7 changes: 2 additions & 5 deletions common/web/es-bundling/src/configuration.mts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@ import type * as esbuild from 'esbuild';
import { pluginForDowncompiledClassTreeshaking } from './classTreeshaker.mjs';

export const esmConfiguration: esbuild.BuildOptions = {
alias: {
'tslib': '@keymanapp/tslib'
},
bundle: true,
format: "esm",
outExtension: { '.js': '.mjs'},
plugins: [ pluginForDowncompiledClassTreeshaking ],
sourcemap: true,
sourcesContent: true,
target: "es5"
target: "es6"
};

export const iifeConfiguration: esbuild.BuildOptions = {
Expand Down Expand Up @@ -70,4 +67,4 @@ export function bundleObjEntryPoints(configFolder: 'lib' | 'debug' | 'release',
entryPoints: path,
outdir: mappedRoot
};
}
}
24 changes: 17 additions & 7 deletions common/web/keyboard-processor/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE

. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
. "${KEYMAN_ROOT}/resources/shellHelperFunctions.sh"

BUNDLE_CMD="node $KEYMAN_ROOT/common/web/es-bundling/build/common-bundle.mjs"
BUNDLE_CMD="node ${KEYMAN_ROOT}/common/web/es-bundling/build/common-bundle.mjs"

################################ Main script ################################

Expand Down Expand Up @@ -42,25 +42,35 @@ function do_configure() {
}

function do_build() {
tsc --build "$THIS_SCRIPT_PATH/tsconfig.all.json"
tsc --build "${THIS_SCRIPT_PATH}/tsconfig.all.json"

# Base product - the main keyboard processor
$BUNDLE_CMD "${KEYMAN_ROOT}/common/web/keyboard-processor/build/obj/index.js" \
builder_echo "Bundle base product - the main keyboard processor"
${BUNDLE_CMD} "${KEYMAN_ROOT}/common/web/keyboard-processor/build/obj/index.js" \
--out "${KEYMAN_ROOT}/common/web/keyboard-processor/build/lib/index.mjs" \
--format esm

# The DOM-oriented keyboard loader
$BUNDLE_CMD "${KEYMAN_ROOT}/common/web/keyboard-processor/build/obj/keyboards/loaders/dom-keyboard-loader.js" \
builder_echo "Bundle the DOM-oriented keyboard loader"
${BUNDLE_CMD} "${KEYMAN_ROOT}/common/web/keyboard-processor/build/obj/keyboards/loaders/dom-keyboard-loader.js" \
--out "${KEYMAN_ROOT}/common/web/keyboard-processor/build/lib/dom-keyboard-loader.mjs" \
--format esm

# The Node-oriented keyboard loader
$BUNDLE_CMD "${KEYMAN_ROOT}/common/web/keyboard-processor/build/obj/keyboards/loaders/node-keyboard-loader.js" \
builder_echo "Bundle the Node-oriented keyboard loader"
${BUNDLE_CMD} "${KEYMAN_ROOT}/common/web/keyboard-processor/build/obj/keyboards/loaders/node-keyboard-loader.js" \
--out "${KEYMAN_ROOT}/common/web/keyboard-processor/build/lib/node-keyboard-loader.mjs" \
--format esm \
--platform node

# Tests
builder_echo "Bundle tests"
${BUNDLE_CMD} "${KEYMAN_ROOT}/common/web/keyboard-processor/build/tests/dom/cases/domKeyboardLoader.spec.js" \
--out "${KEYMAN_ROOT}/common/web/keyboard-processor/build/tests/dom/domKeyboardLoader.spec.mjs" \
--format esm

# Declaration bundling.
builder_echo "Declaration bundling"
tsc --emitDeclarationOnly --outFile ./build/lib/index.d.ts
tsc --emitDeclarationOnly --outFile ./build/lib/dom-keyboard-loader.d.ts -p src/keyboards/loaders/tsconfig.dom.json
tsc --emitDeclarationOnly --outFile ./build/lib/node-keyboard-loader.d.ts -p src/keyboards/loaders/tsconfig.node.json
Expand All @@ -82,4 +92,4 @@ function do_test() {
builder_run_action configure do_configure
builder_run_action clean rm -rf ./build
builder_run_action build do_build
builder_run_action test do_test
builder_run_action test do_test
9 changes: 4 additions & 5 deletions common/web/keyboard-processor/src/text/stringDivergence.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// Future TODO: import from @keymanapp/common-types... once we no longer need to support ES5.
import { Uni_IsSurrogate1, Uni_IsSurrogate2 } from '@keymanapp/web-utils';
import { util } from '@keymanapp/common-types';
Copy link
Contributor

@jahorton jahorton Jul 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requiring this style of import for the utility methods prevents treeshaking from working as well as it could. Having to import the Uni_IsSurrogate* functions through a higher-level structure (the collective export * as util pattern) results in esbuild importing all functions exported from that structure.

Tracking issue: evanw/esbuild#1420

This can be found within the debug build of web's app/browser when built:

Warning - long code block! (click to expand)
  // ../../../../common/web/types/src/util/util.ts
  var util_exports = {};
  __export(util_exports, {
    BadStringAnalyzer: () => BadStringAnalyzer,
    BadStringType: () => BadStringType,
    CONTAINS_QUAD_ESCAPE: () => CONTAINS_QUAD_ESCAPE,
    MATCH_HEX_ESCAPE: () => MATCH_HEX_ESCAPE,
    MATCH_QUAD_ESCAPE: () => MATCH_QUAD_ESCAPE,
    NFDAnalyzer: () => NFDAnalyzer,
    StringAnalyzer: () => StringAnalyzer,
    UnescapeError: () => UnescapeError,
    Uni_IsSurrogate: () => Uni_IsSurrogate,
    Uni_IsSurrogate1: () => Uni_IsSurrogate1,
    Uni_IsSurrogate2: () => Uni_IsSurrogate2,
    boxXmlArray: () => boxXmlArray,
    describeCodepoint: () => describeCodepoint,
    escapeRegexChar: () => escapeRegexChar,
    escapeStringForRegex: () => escapeStringForRegex,
    hexOcts: () => hexOcts,
    hexQuad: () => hexQuad,
    isOneChar: () => isOneChar,
    isPUA: () => isPUA,
    isValidUnicode: () => isValidUnicode,
    toOneChar: () => toOneChar,
    unescapeOne: () => unescapeOne,
    unescapeOneQuadString: () => unescapeOneQuadString,
    unescapeQuadString: () => unescapeQuadString,
    unescapeString: () => unescapeString,
    unescapeStringToRegex: () => unescapeStringToRegex
  });
  function boxXmlArray(o, x) {
    if (typeof o == "object" && !Array.isArray(o[x])) {
      if (o[x] === null || o[x] === void 0) {
        o[x] = [];
      } else {
        o[x] = [o[x]];
      }
    }
  }
  __name(boxXmlArray, "boxXmlArray");
  var MATCH_HEX_ESCAPE = /\\u{([0-9a-fA-F ]{1,})}/g;
  var CONTAINS_QUAD_ESCAPE = /(?:\\u([0-9a-fA-F]{4})|\\U([0-9a-fA-F]{8}))/;
  var MATCH_QUAD_ESCAPE = new RegExp(CONTAINS_QUAD_ESCAPE, "g");
  var _UnescapeError = class _UnescapeError extends Error {
  };
  __name(_UnescapeError, "UnescapeError");
  var UnescapeError = _UnescapeError;
  function unescapeOne(hex) {
    const codepoint = Number.parseInt(hex, 16);
    return String.fromCodePoint(codepoint);
  }
  __name(unescapeOne, "unescapeOne");
  function unescapeOneQuadString(s) {
    if (!s || !s.match(MATCH_QUAD_ESCAPE)) {
      throw new UnescapeError(`Not a quad escape: ${s}`);
    }
    function processMatch(str, m16, m32) {
      return unescapeOne(m16 || m32);
    }
    __name(processMatch, "processMatch");
    s = s.replace(MATCH_QUAD_ESCAPE, processMatch);
    return s;
  }
  __name(unescapeOneQuadString, "unescapeOneQuadString");
  function unescapeQuadString(s) {
    s = s.replaceAll(MATCH_QUAD_ESCAPE, (quad) => unescapeOneQuadString(quad));
    return s;
  }
  __name(unescapeQuadString, "unescapeQuadString");
  function unescapeString(s) {
    if (!s) {
      return s;
    }
    try {
      let processMatch2 = function(str, matched) {
        const codepoints = matched.split(" ");
        const unescaped = codepoints.map(unescapeOne);
        return unescaped.join("");
      };
      var processMatch = processMatch2;
      __name(processMatch2, "processMatch");
      s = s.replaceAll(MATCH_HEX_ESCAPE, processMatch2);
    } catch (e) {
      if (e instanceof RangeError) {
        throw new UnescapeError(`Out of range while unescaping '${s}': ${e.message}`, { cause: e });
      } else {
        throw e;
      }
    }
    return s;
  }
  __name(unescapeString, "unescapeString");
  function hexQuad(n) {
    if (n < 0 || n > 65535) {
      throw RangeError(`${n} not in [0x0000,0xFFFF]`);
    }
    return n.toString(16).padStart(4, "0");
  }
  __name(hexQuad, "hexQuad");
  function hexOcts(n) {
    if (n < 0 || n > 4294967295) {
      throw RangeError(`${n} not in [0x00000000,0xFFFFFFFF]`);
    }
    return n.toString(16).padStart(8, "0");
  }
  __name(hexOcts, "hexOcts");
  function escapeRegexChar(ch) {
    const code = ch.codePointAt(0);
    if (code <= 65535) {
      return "\\u" + hexQuad(code);
    } else {
      return "\\U" + hexOcts(code);
    }
  }
  __name(escapeRegexChar, "escapeRegexChar");
  var REGEX_SYNTAX_CHAR = /^[\u0000-\u001F\u007F-\u009F{}\[\]\\?|.^$*()/+-]$/;
  function escapeRegexCharIfSyntax(ch) {
    if (REGEX_SYNTAX_CHAR.test(ch) || !isValidUnicode(ch.codePointAt(0))) {
      return escapeRegexChar(ch);
    } else {
      return ch;
    }
  }
  __name(escapeRegexCharIfSyntax, "escapeRegexCharIfSyntax");
  function regexOne(hex) {
    const unescaped = unescapeOne(hex);
    return Array.from(unescaped).map((ch) => escapeRegexCharIfSyntax(ch)).join("");
  }
  __name(regexOne, "regexOne");
  function escapeStringForRegex(s) {
    return s.split("").map((ch) => escapeRegexCharIfSyntax(ch)).join("");
  }
  __name(escapeStringForRegex, "escapeStringForRegex");
  function unescapeStringToRegex(s) {
    if (!s) {
      return s;
    }
    try {
      let processMatch2 = function(str, matched) {
        const codepoints = matched.split(" ");
        const unescaped = codepoints.map(regexOne);
        return unescaped.join("");
      };
      var processMatch = processMatch2;
      __name(processMatch2, "processMatch");
      s = s.replaceAll(MATCH_HEX_ESCAPE, processMatch2);
    } catch (e) {
      if (e instanceof RangeError) {
        throw new UnescapeError(`Out of range while unescaping '${s}': ${e.message}`, { cause: e });
      } else {
        throw e;
      }
    }
    return s;
  }
  __name(unescapeStringToRegex, "unescapeStringToRegex");
  function isOneChar(value) {
    return [...value].length === 1;
  }
  __name(isOneChar, "isOneChar");
  function toOneChar(value) {
    if (!isOneChar(value)) {
      throw Error(`Not a single char: ${value}`);
    }
    return value.codePointAt(0);
  }
  __name(toOneChar, "toOneChar");
  function describeCodepoint(ch) {
    let s;
    const p = BadStringAnalyzer.getProblem(ch);
    if (p != null) {
      s = p;
    } else {
      s = `"${String.fromCodePoint(ch)}"`;
    }
    return `${s} (U+${Number(ch).toString(16).toUpperCase()})`;
  }
  __name(describeCodepoint, "describeCodepoint");
  var BadStringType = /* @__PURE__ */ ((BadStringType2) => {
    BadStringType2["pua"] = "PUA";
    BadStringType2["unassigned"] = "Unassigned";
    BadStringType2["illegal"] = "Illegal";
    BadStringType2["denormalized"] = "Denormalized";
    return BadStringType2;
  })(BadStringType || {});
  var Uni_LEAD_SURROGATE_START = 55296;
  var Uni_LEAD_SURROGATE_END = 56319;
  var Uni_TRAIL_SURROGATE_START = 56320;
  var Uni_TRAIL_SURROGATE_END = 57343;
  var Uni_SURROGATE_START = Uni_LEAD_SURROGATE_START;
  var Uni_SURROGATE_END = Uni_TRAIL_SURROGATE_END;
  var Uni_FD_NONCHARACTER_START = 64976;
  var Uni_FD_NONCHARACTER_END = 65007;
  var Uni_FFFE_NONCHARACTER = 65534;
  var Uni_PLANE_MASK = 2031616;
  var Uni_MAX_CODEPOINT = 1114111;
  var Uni_PUA_00_START = 57344;
  var Uni_PUA_00_END = 63743;
  var Uni_PUA_15_START = 983040;
  var Uni_PUA_15_END = 1048573;
  var Uni_PUA_16_START = 1048576;
  var Uni_PUA_16_END = 1114109;
  function Uni_IsSurrogate1(ch) {
    return ch >= Uni_LEAD_SURROGATE_START && ch <= Uni_LEAD_SURROGATE_END;
  }
  __name(Uni_IsSurrogate1, "Uni_IsSurrogate1");
  function Uni_IsSurrogate2(ch) {
    return ch >= Uni_TRAIL_SURROGATE_START && ch <= Uni_TRAIL_SURROGATE_END;
  }
  __name(Uni_IsSurrogate2, "Uni_IsSurrogate2");
  function Uni_IsSurrogate(ch) {
    return Uni_IsSurrogate1(ch) || Uni_IsSurrogate2(ch);
  }
  __name(Uni_IsSurrogate, "Uni_IsSurrogate");
  function Uni_IsEndOfPlaneNonCharacter(ch) {
    return (ch & Uni_FFFE_NONCHARACTER) == Uni_FFFE_NONCHARACTER;
  }
  __name(Uni_IsEndOfPlaneNonCharacter, "Uni_IsEndOfPlaneNonCharacter");
  function Uni_IsNoncharacter(ch) {
    return ch >= Uni_FD_NONCHARACTER_START && ch <= Uni_FD_NONCHARACTER_END || Uni_IsEndOfPlaneNonCharacter(ch);
  }
  __name(Uni_IsNoncharacter, "Uni_IsNoncharacter");
  function Uni_InCodespace(ch) {
    return ch >= 0 && ch <= Uni_MAX_CODEPOINT;
  }
  __name(Uni_InCodespace, "Uni_InCodespace");
  function Uni_IsValid1(ch) {
    return Uni_InCodespace(ch) && !Uni_IsSurrogate(ch) && !Uni_IsNoncharacter(ch);
  }
  __name(Uni_IsValid1, "Uni_IsValid1");
  function isValidUnicode(start, end) {
    if (!end) {
      return Uni_IsValid1(start);
    } else if (!Uni_IsValid1(end) || !Uni_IsValid1(start) || end < start) {
      return false;
    } else if (start <= Uni_SURROGATE_END && end >= Uni_SURROGATE_START) {
      return false;
    } else if (start <= Uni_FD_NONCHARACTER_END && end >= Uni_FD_NONCHARACTER_START) {
      return false;
    } else if ((start & Uni_PLANE_MASK) != (end & Uni_PLANE_MASK)) {
      return false;
    } else {
      return true;
    }
  }
  __name(isValidUnicode, "isValidUnicode");
  function isPUA(ch) {
    return ch >= Uni_PUA_00_START && ch <= Uni_PUA_00_END || ch >= Uni_PUA_15_START && ch <= Uni_PUA_15_END || ch >= Uni_PUA_16_START && ch <= Uni_PUA_16_END;
  }
  __name(isPUA, "isPUA");
  var _BadStringMap = class _BadStringMap extends Map {
    toString() {
      if (!this.size) {
        return "{}";
      }
      return Array.from(this.entries()).map(([t, s]) => `${t}: ${Array.from(s.values()).map(describeCodepoint).join(" ")}`).join(", ");
    }
  };
  __name(_BadStringMap, "BadStringMap");
  var BadStringMap = _BadStringMap;
  var _StringAnalyzer = class _StringAnalyzer {
    constructor() {
      /** internal map */
      __publicField(this, "m", new BadStringMap());
    }
    /** add a string for analysis */
    add(s) {
      for (const c of [...s]) {
        const ch = c.codePointAt(0);
        const problem = this.analyzeCodePoint(c, ch);
        if (problem) {
          this.addProblem(ch, problem);
        }
      }
    }
    /** internal interface for the result of an analysis */
    addProblem(ch, type) {
      if (!this.m.has(type)) {
        this.m.set(type, /* @__PURE__ */ new Set());
      }
      this.m.get(type).add(ch);
    }
    /** get the results of the analysis */
    analyze() {
      if (this.m.size == 0) {
        return null;
      } else {
        return this.m;
      }
    }
  };
  __name(_StringAnalyzer, "StringAnalyzer");
  var StringAnalyzer = _StringAnalyzer;
  var _BadStringAnalyzer = class _BadStringAnalyzer extends StringAnalyzer {
    /** analyze one codepoint */
    analyzeCodePoint(c, ch) {
      return _BadStringAnalyzer.getProblem(ch);
    }
    /** export analyzer function  */
    static getProblem(ch) {
      if (!isValidUnicode(ch)) {
        return "Illegal" /* illegal */;
      } else if (isPUA(ch)) {
        return "PUA" /* pua */;
      } else {
        return null;
      }
    }
  };
  __name(_BadStringAnalyzer, "BadStringAnalyzer");
  var BadStringAnalyzer = _BadStringAnalyzer;
  var _NFDAnalyzer = class _NFDAnalyzer extends StringAnalyzer {
    analyzeCodePoint(c, ch) {
      const nfd = c.normalize("NFD");
      if (c !== nfd) {
        return "Denormalized" /* denormalized */;
      } else {
        return null;
      }
    }
  };
  __name(_NFDAnalyzer, "NFDAnalyzer");
  var NFDAnalyzer = _NFDAnalyzer;

Copy link
Contributor

@jahorton jahorton Jul 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I instead import those functions directly from the original, defining source file...

  // ../../../../common/web/types/src/util/util.ts
  var CONTAINS_QUAD_ESCAPE = /(?:\\u([0-9a-fA-F]{4})|\\U([0-9a-fA-F]{8}))/;
  var MATCH_QUAD_ESCAPE = new RegExp(CONTAINS_QUAD_ESCAPE, "g");
  var Uni_LEAD_SURROGATE_START = 55296;
  var Uni_LEAD_SURROGATE_END = 56319;
  var Uni_TRAIL_SURROGATE_START = 56320;
  var Uni_TRAIL_SURROGATE_END = 57343;
  function Uni_IsSurrogate1(ch) {
    return ch >= Uni_LEAD_SURROGATE_START && ch <= Uni_LEAD_SURROGATE_END;
  }
  __name(Uni_IsSurrogate1, "Uni_IsSurrogate1");
  function Uni_IsSurrogate2(ch) {
    return ch >= Uni_TRAIL_SURROGATE_START && ch <= Uni_TRAIL_SURROGATE_END;
  }
  __name(Uni_IsSurrogate2, "Uni_IsSurrogate2");

Way less imported code this way - easily enough to account for a few KB in file-size difference even when minified.

Alternatively, exporting the two explicitly from common/web/types/src/main.ts outside of a "namespace" export would also do the job. Could be "in addition to" to keep the overall change set minimal, rather than removing it from the util namespace currently used by other consumers.

Copy link
Contributor

@jahorton jahorton Jul 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively... we could just leave the versions defined within common/web/utils in place, as they're not subject to the import pattern causing the issue.

Alternatively-alternatively, we could expose the util.js file as its own... "sub-export"(?) from common/web/types. It's a pattern I'm comfortable implementing if we want to go that route. We'd then be able to import { whatever } from '@keymanapp/common-types/utils' or similar. (We can choose what we name that final path component.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented the alternative alternative. With that the size stays the same.


/**
* Returns the index for the code point divergence point between two strings, as measured in code
Expand Down Expand Up @@ -73,8 +72,8 @@ export function findCommonSubstringEndIndex(str1: string, str2: string, commonSu
const divergentChar1 = str1.charCodeAt(index);
const divergentChar2 = str2.charCodeAt(index + offset);

const commonSurrogateChecker = commonSuffix ? Uni_IsSurrogate2 : Uni_IsSurrogate1;
const divergentSurrogateChecker = commonSuffix ? Uni_IsSurrogate1 : Uni_IsSurrogate2;
const commonSurrogateChecker = commonSuffix ? util.Uni_IsSurrogate2 : util.Uni_IsSurrogate1;
const divergentSurrogateChecker = commonSuffix ? util.Uni_IsSurrogate1 : util.Uni_IsSurrogate2;

// If the last common character if of the direction-appropriate surrogate type (for
// comprising a potential split surrogate pair representing a non-BMP char)...
Expand All @@ -90,4 +89,4 @@ export function findCommonSubstringEndIndex(str1: string, str2: string, commonSu
}

return index;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { assert } from 'chai';
import { DOMKeyboardLoader } from '@keymanapp/keyboard-processor/dom-keyboard-loader';
import { extendString, KeyboardHarness, Keyboard, KeyboardInterface, MinimalKeymanGlobal, Mock, DeviceSpec } from '@keymanapp/keyboard-processor';

type WindowKey = keyof typeof window;
const keyman_window = 'keyman' as WindowKey;
const KeymanWeb = 'KeymanWeb' as WindowKey;

// Note: rule processing tests will fail if string extensions are not established beforehand.
extendString();

Expand All @@ -15,8 +19,8 @@ const device: DeviceSpec = {

describe('Keyboard loading in DOM', function() {
afterEach(() => {
if(window['KeymanWeb']) {
window['KeymanWeb'].uninstall();
if (window[KeymanWeb]) {
(window[KeymanWeb] as KeyboardInterface).uninstall();
}
})

Expand All @@ -29,8 +33,8 @@ describe('Keyboard loading in DOM', function() {
assert.equal(keyboard.id, 'Keyboard_khmer_angkor');
assert.isTrue(keyboard.isChiral);
assert.isFalse(keyboard.isCJK);
assert.isOk(window['KeymanWeb']);
assert.isOk(window['keyman']);
assert.isOk(window[KeymanWeb]);
assert.isOk(window[keyman_window]);

// Should be cleared post-keyboard-load.
assert.isNotOk(harness.loadedKeyboard);
Expand All @@ -46,17 +50,17 @@ describe('Keyboard loading in DOM', function() {
assert.equal(keyboard.id, 'Keyboard_khmer_angkor');
assert.isTrue(keyboard.isChiral);
assert.isFalse(keyboard.isCJK);
assert.isOk(window['KeymanWeb']);
assert.isOk(window['keyman']);
assert.isOk(window[KeymanWeb]);
assert.isOk(window[keyman_window]);

// TODO: verify actual rule processing.
const nullKeyEvent = keyboard.constructNullKeyEvent(device);
const mock = new Mock();
const result = harness.processKeystroke(mock, nullKeyEvent);

assert.isOk(result);
assert.isOk(window['KeymanWeb']);
assert.isOk(window['keyman']);
assert.isOk(window[KeymanWeb]);
assert.isOk(window[keyman_window]);

// Should be cleared post-keyboard-load.
assert.isNotOk(harness.loadedKeyboard);
Expand Down Expand Up @@ -95,4 +99,4 @@ describe('Keyboard loading in DOM', function() {

harness.activeKeyboard = lao_keyboard;
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default {
concurrency: 10,
nodeResolve: true,
files: [
'**/*.spec.ts'
'build/tests/dom/**/*.spec.mjs'
],
middleware: [
// Rewrites short-hand paths for test resources, making them fully relative to the repo root.
Expand Down Expand Up @@ -59,4 +59,4 @@ export default {
// open: true,
// manual: true,
rootDir: KEYMAN_ROOT
}
}
11 changes: 11 additions & 0 deletions common/web/keyboard-processor/tests/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"baseUrl": "../",
"outDir": "../build/tests/",
"tsBuildInfoFile": "../build/tests/tsconfig.tsbuildinfo",
"rootDir": "./"
},
"include": [ "./dom/**/*.ts"],
"exclude": []
}
5 changes: 3 additions & 2 deletions common/web/keyboard-processor/tsconfig.all.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
"baseUrl": "./",
"outDir": "build/obj/",
"tsBuildInfoFile": "build/obj/tsconfig.all.tsbuildinfo",
"rootDir": "./src"
"rootDir": "./src/"
},
"references": [
{ "path": "./src/keyboards/loaders/tsconfig.dom.json" },
{ "path": "./src/keyboards/loaders/tsconfig.node.json" }
{ "path": "./src/keyboards/loaders/tsconfig.node.json" },
{ "path": "./tests/tsconfig.json" },
],
// Actual main-body compilation is in tsconfig.json. This config is just a wrapper
// to trigger all three components at once.
Expand Down
4 changes: 2 additions & 2 deletions common/web/keyboard-processor/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
"baseUrl": "./",
"outDir": "build/obj/",
"tsBuildInfoFile": "build/obj/tsconfig.tsbuildinfo",
"rootDir": "./src"
"rootDir": "./src/"
},
"references": [
{ "path": "../types" },
{ "path": "../../models/types" },
{ "path": "../keyman-version/" },
{ "path": "../utils/" }
],
"include": ["./src/**/*.ts"],
"include": [ "./src/**/*.ts"],
"exclude": ["./src/keyboards/loaders/**/*.ts"]
}
Loading