Merge pull request #9681 from keymanapp/chore/common/9262-cleanup-final-non-esm

chore(common): cleanup final Typescript non-ESM metadata
This commit is contained in:
Marc Durdin 2023-10-06 13:37:54 +11:00 committed by GitHub
commit a1d7095c58
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
51 changed files with 78 additions and 2766 deletions

View file

@ -21,9 +21,7 @@
"exports": {
".": "./build/obj/index.js",
"./lib": {
"types": "./build/lib/index.d.ts",
"import": "./build/lib/index.mjs",
"require": "./build/lib/index.cjs"
"types": "./build/lib/index.d.ts"
},
"./obj/*.js": "./build/obj/*.js"
},

View file

@ -19,8 +19,7 @@
"exports": {
".": "./build/obj/index.js",
"./lib": {
"import": "./build/lib/index.mjs",
"require": "./build/lib/index.cjs"
"types": "./build/lib/index.d.ts"
},
"./obj/*.js": "./build/obj/*.js"
},

View file

@ -1,28 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<!-- Set the viewport width to match iOS device widths -->
<!-- <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no" /> -->
<meta name="viewport" content="width=device-width,user-scalable=no" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<!-- Enable IE9 Standards mode -->
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>LMLayer Testing</title>
<style type='text/css'>
body {padding-left:20px;margin-left:12px; font-family:Tahoma,Helvetica}
h1 {color: #800;margin-left:10px;}
h2 {color: #008;margin-left:20px;}
</style>
</head>
<body>
<h1>Language-Modeling Layer module testing</h1>
<h2><a href="./simple-webworker">A simple basic WebWorker.</a></h2> Designed as a baseline functionality test we can run against various platforms as a first-stage canary of sorts.
<h2><a href="./one-stage-embedded-webworker">An embedded WebWorker prototype.</a></h2> A prototype for directly embedding a WebWorker's code within a "master" script.
<h2><a href="./two-stage-embedded-webworker">A better embedded WebWorker prototype.</a></h2> A prototype for two-stage compilation of a master/slave main-script/WebWorker pair.
<h2><a href="./post-blob-webworker/">Sending code via a Blob URI</a></h2> A prototype for sending code over to a WebWorker via a Blob URI reference.
</body>
</html>

View file

@ -1,2 +0,0 @@
main.js
main.js.map

View file

@ -1,54 +0,0 @@
#!/usr/bin/env bash
#
# Compiles the Language Modeling Layer for common use in predictive text and autocorrective applications.
# Designed for optimal compatibility with the Keyman Suite.
#
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../../resources/build/build-utils.sh"
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
## END STANDARD BUILD SCRIPT INCLUDE
display_usage ( ) {
echo "build.sh [-clean]"
echo
echo " -clean to erase pre-existing build products before a re-build"
}
SOURCE="testing/one-stage-embedded-webworker"
echo "Node.js + dependencies check"
npm install --no-optional
if [ $? -ne 0 ]; then
builder_die "Build environment setup error detected! Please ensure Node.js is installed!"
fi
# A nice, extensible method for -clean operations. Add to this as necessary.
clean ( ) {
rm -rf "./*.js"
if [ $? -ne 0 ]; then
builder_die "Failed to erase the prior build."
fi
}
# Process command-line arguments
while [[ $# -gt 0 ]] ; do
key="$1"
case $key in
-clean)
clean
;;
esac
shift # past the processed argument
done
npm run tsc -- -p $SOURCE/tsconfig.json
if [ $? -ne 0 ]; then
builder_die "Compilation failed."
fi
echo "Typescript compilation successful."

View file

@ -1,42 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<!-- Set the viewport width to match phone and tablet device widths -->
<meta name="viewport" content="width=device-width,user-scalable=no" />
<!-- Allow KeymanWeb to be saved to the iPhone home screen -->
<meta name="apple-mobile-web-app-capable" content="yes" />
<!-- Enable IE9 Standards mode -->
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Embedded WebWorker test</title>
<!-- Your page CSS -->
<style type='text/css'>
body {font-family: Tahoma,helvetica;}
h3 {font-size: 1em;font-weight:normal;color: darkred; margin-bottom: 4px}
.test {font-size: 1.5em; width:80%; min-height:30px; border: 1px solid gray;}
#KeymanWebControl {width:50%;min-width:600px;}
</style>
<script src="main.js"></script>
</head>
<!-- Sample page HTML -->
<body>
<h2>LM Layer Testing - One-Stage Embedded WebWorkers</h2>
<p>This page serves as a prototype for embedding a WebWorker's code within its "master" script
across the TypeScript transpilation boundary.</p>
<input type='text' id='txtFeedback' value="No clicks yet." readonly></input> <br>
<script>
var txtFeedback = document.getElementById("txtFeedback");
txtFeedback.value = "No clicks yet.";
</script>
<input type='button' id='btnInput' onclick='canaryWorker.postMessage("Hello.");' value='Message the WebWorker.' />
<h3><a href="../index.html">Return to testing home page</a></h3>
</body>
</html>

View file

@ -1,52 +0,0 @@
// Useful for passing class constructors
type Workable<T> = {
new (...args: any[]): T;
// Requires a static implementation.
onmessage(e: any): void;
};
class A {
a(x: number, y: number):number {
return x + y;
}
}
var WorkerGlobals = {
counter: 0,
a: A
}
class WorkerCore {
static WorkerGlobals = WorkerGlobals;
//static counter: number;
static onmessage(e: any) {
WorkerGlobals.counter++;
console.log("Message received from main page: ", e.data);
// Forces TypeScript to interpret this line as plain JavaScript, as it uses a non-Worker definition.
// @ts-ignore
postMessage(WorkerGlobals.counter);
}
}
function createWorkerFromClasses(globals: object, fn: Workable<object>): Worker {
var sep = ";\n";
let glb = "var WorkerGlobals = " + JSON.stringify(globals);
let wc = "var onmessage = " + fn.onmessage.toString();
var blob = new Blob([glb, sep, wc], { type: 'text/javascript' });
let url = URL.createObjectURL(blob);
return new Worker(url);
}
var canaryWorker = createWorkerFromClasses(WorkerGlobals, WorkerCore);
canaryWorker.onmessage = function(e) {
var counter = e.data; // Number of times the WebWorker has been messaged.
console.log("Received message from the WebWorker: " + e.data);
var txtFeedback = <HTMLInputElement>document.getElementById("txtFeedback");
txtFeedback.value = counter + " click(s)";
}

View file

@ -1,13 +0,0 @@
{
"compilerOptions": {
"allowJs": false,
"module": "none",
"outDir": "./",
"inlineSources": true,
"sourceMap": true,
"target": "es5"
},
"files" : [
"main.ts"
]
}

View file

@ -1,5 +0,0 @@
// receive a message and execute its uri
onmessage = function (e) {
let uri = e.data.uri;
importScripts(uri);
};

View file

@ -1,72 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<!-- Set the viewport width to match phone and tablet device widths -->
<meta name="viewport" content="width=device-width,user-scalable=no" />
<!-- Allow KeymanWeb to be saved to the iPhone home screen -->
<meta name="apple-mobile-web-app-capable" content="yes" />
<!-- Enable IE9 Standards mode -->
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Blob URI test</title>
<!-- Your page CSS -->
<style type='text/css'>
body {font-family: Tahoma,helvetica;}
h3 {font-size: 1em;font-weight:normal;color: darkred; margin-bottom: 4px}
.test {font-size: 1.5em; width:80%; min-height:30px; border: 1px solid gray;}
#KeymanWebControl {width:50%;min-width:600px;}
</style>
<script>
if (window.Worker) {
var canaryWorker = new Worker('canaryWorker.js');
canaryWorker.onmessage = function(e) {
var message = e.data; // Number of times the WebWorker has been messaged.
console.log("Received message from the WebWorker: " + e.data);
var txtFeedback = document.getElementById("txtFeedback");
txtFeedback.value = message;
}
} else {
console.error("WebWorkers are not supported in this browser!");
}
</script>
</head>
<!-- Sample page HTML -->
<body>
<h2>LM Layer Testing - Sending a function to a WebWorker via a Blob URI test</h2>
<p>This page sends a function over to the Worker via a Blob URI, and
expects the function to reply back.
</p>
<input type='text' id='txtFeedback' value="Haven't heard back yet" readonly> <br>
<script>
var txtFeedback = document.getElementById("txtFeedback");
txtFeedback.value = "Have not heard back from worker";
function sendCode () {
function payload () {
postMessage('Hello, from the blobified code!');
}
// Create an immediately-invoked function expression... to immediately
// invoke the code!
let iife = ['(', payload.toString(), '())'];
let blob = new Blob(iife, { type: 'text/javascript' });
let uri = URL.createObjectURL(blob);
// Go!
canaryWorker.postMessage({ uri: uri });
}
</script>
<input type='button' id='btnInput' onclick='sendCode()' value='Send the source code!' />
<h3><a href="../index.html">Return to testing home page</a></h3>
</body>
</html>

View file

@ -1,9 +0,0 @@
var counter = 0;
onmessage = function(e) {
counter++;
console.log("Message received from main page: ", e.data);
postMessage(counter);
}

View file

@ -1,55 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<!-- Set the viewport width to match phone and tablet device widths -->
<meta name="viewport" content="width=device-width,user-scalable=no" />
<!-- Allow KeymanWeb to be saved to the iPhone home screen -->
<meta name="apple-mobile-web-app-capable" content="yes" />
<!-- Enable IE9 Standards mode -->
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Basic WebWorker test</title>
<!-- Your page CSS -->
<style type='text/css'>
body {font-family: Tahoma,helvetica;}
h3 {font-size: 1em;font-weight:normal;color: darkred; margin-bottom: 4px}
.test {font-size: 1.5em; width:80%; min-height:30px; border: 1px solid gray;}
#KeymanWebControl {width:50%;min-width:600px;}
</style>
<script>
if(window.Worker) {
var canaryWorker = new Worker('canaryWorker.js');
canaryWorker.onmessage = function(e) {
var counter = e.data; // Number of times the WebWorker has been messaged.
console.log("Received message from the WebWorker: " + e.data);
var txtFeedback = document.getElementById("txtFeedback");
txtFeedback.value = counter + " click(s)";
}
} else {
console.error("WebWorkers are not supported in this browser!");
}
</script>
</head>
<!-- Sample page HTML -->
<body>
<h2>LM Layer Testing - basic WebWorker canary</h2>
<p>This page uses a simple WebWorker useful for ensuring functionality on various platforms.</p>
<input type='text' id='txtFeedback' value="No clicks yet." readonly></input> <br>
<script>
var txtFeedback = document.getElementById("txtFeedback");
txtFeedback.value = "No clicks yet.";
</script>
<input type='button' id='btnInput' onclick='canaryWorker.postMessage("Hello.");' value='Message the WebWorker.' />
<h3><a href="../index.html">Return to testing home page</a></h3>
</body>
</html>

View file

@ -1,2 +0,0 @@
**/*.js
**/*.js.map

View file

@ -1,68 +0,0 @@
#!/usr/bin/env bash
#
# Compiles the Language Modeling Layer for common use in predictive text and autocorrective applications.
# Designed for optimal compatibility with the Keyman Suite.
#
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../../resources/build/build-utils.sh"
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
## END STANDARD BUILD SCRIPT INCLUDE
display_usage ( ) {
echo "build.sh [-clean]"
echo
echo " -clean to erase pre-existing build products before a re-build"
}
SOURCE="testing/two-stage-embedded-webworker"
COMPILED_WORKER="worker.js"
EMBEDDED_WORKER="embedded_worker.js"
echo "Node.js + dependencies check"
npm install --no-optional
if [ $? -ne 0 ]; then
builder_die "Build environment setup error detected! Please ensure Node.js is installed!"
fi
# A nice, extensible method for -clean operations. Add to this as necessary.
clean ( ) {
rm -rf "./*.js"
if [ $? -ne 0 ]; then
builder_die "Failed to erase the prior build."
fi
}
# Process command-line arguments
while [[ $# -gt 0 ]] ; do
key="$1"
case $key in
-clean)
clean
;;
esac
shift # past the processed argument
done
npm run tsc -- -p $SOURCE/worker/tsconfig.json
if [ $? -ne 0 ]; then
builder_die "Worker compilation failed."
fi
rm $EMBEDDED_WORKER &> /dev/null
echo "var LMLayerWorker = function() {" >> $EMBEDDED_WORKER
cat $COMPILED_WORKER >> $EMBEDDED_WORKER
echo "" >> $EMBEDDED_WORKER
echo "}" >> $EMBEDDED_WORKER
npm run tsc -- -p $SOURCE/tsconfig.json
if [ $? -ne 0 ]; then
builder_die "Final compilation failed."
fi
echo "Typescript compilation successful."

View file

@ -1,42 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<!-- Set the viewport width to match phone and tablet device widths -->
<meta name="viewport" content="width=device-width,user-scalable=no" />
<!-- Allow KeymanWeb to be saved to the iPhone home screen -->
<meta name="apple-mobile-web-app-capable" content="yes" />
<!-- Enable IE9 Standards mode -->
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Embedded WebWorker test</title>
<!-- Your page CSS -->
<style type='text/css'>
body {font-family: Tahoma,helvetica;}
h3 {font-size: 1em;font-weight:normal;color: darkred; margin-bottom: 4px}
.test {font-size: 1.5em; width:80%; min-height:30px; border: 1px solid gray;}
#KeymanWebControl {width:50%;min-width:600px;}
</style>
<script src="main.js"></script>
</head>
<!-- Sample page HTML -->
<body>
<h2>LM Layer Testing - One-Stage Embedded WebWorkers</h2>
<p>This page serves as a prototype for embedding a WebWorker's code within its "master" script
across the TypeScript transpilation boundary.</p>
<input type='text' id='txtFeedback' value="No clicks yet." readonly></input> <br>
<script>
var txtFeedback = document.getElementById("txtFeedback");
txtFeedback.value = "No clicks yet.";
</script>
<input type='button' id='btnInput' onclick='canaryWorker.postMessage("Hello.");' value='Message the WebWorker.' />
<h3><a href="../index.html">Return to testing home page</a></h3>
</body>
</html>

View file

@ -1,30 +0,0 @@
// Provides the final source for our compiled WebWorker within a wrapping function.
/// <reference path="embedded_worker.js" />
function createWorker(fn: Function): Worker {
var str_fn = fn.toString();
// We now unwrap our WebWorker from its function.
var str_lines = str_fn.split("\n");
var blob_lines = []
for(var i=1; i < str_lines.length -1; i++) {
blob_lines.push(str_lines[i] + "\n");
}
// Unwrapping complete.
var blob = new Blob(blob_lines, { type: 'text/javascript' });
let url = URL.createObjectURL(blob);
return new Worker(url);
}
var canaryWorker = createWorker(LMLayerWorker);
canaryWorker.onmessage = function(e) {
var counter = e.data; // Number of times the WebWorker has been messaged.
console.log("Received message from the WebWorker: " + e.data);
var txtFeedback = <HTMLInputElement>document.getElementById("txtFeedback");
txtFeedback.value = counter + " click(s)";
}

View file

@ -1,13 +0,0 @@
{
"compilerOptions": {
"allowJs": true,
"module": "none",
"outFile": "./main.js",
"inlineSources": true,
"inlineSourceMap": true,
"target": "es5"
},
"files" : [
"main.ts"
]
}

View file

@ -1,9 +0,0 @@
var counter = 0;
onmessage = function(e) {
counter++;
console.log("Message received from main page: ", e.data);
postMessage(counter);
}

View file

@ -1,11 +0,0 @@
{
"compilerOptions": {
"allowJs": false,
"module": "none",
"outFile": "../worker.js",
"inlineSources": true,
"sourceMap": true,
"lib": ["webworker", "es6"],
"target": "es5"
}
}

View file

@ -1,5 +1,5 @@
{
"extends": "../../../tsconfig.esm-base.json",
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"rootDir": ".",
"outDir": "build/"

View file

@ -0,0 +1 @@
declare module 'convert-source-map';

View file

@ -1,12 +1,11 @@
{
"extends": "../../../tsconfig-base.json",
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"allowJs": false,
"allowSyntheticDefaultImports": true,
"declaration": true,
"module": "es6",
"moduleResolution": "node",
"sourceMap": false,
"inlineSourceMap": true,
"inlineSources": true,
"sourceRoot": "/common/tools/sourcemap-path-remapper/src",

View file

@ -1,5 +1,5 @@
{
"extends": "../../../tsconfig.esm-base.json",
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"baseUrl": "./",
"outDir": "build/",

View file

@ -1,5 +1,5 @@
{
"extends": "../../../tsconfig.esm-base.json",
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./build",

View file

@ -1,5 +1,5 @@
{
"extends": "../../../tsconfig-base.json",
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"declaration": true,
"module": "none",

View file

@ -1,13 +1,22 @@
{
"extends": "../../../../tsconfig-base.json",
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {
// TODO: These override /tsconfig.base.json settings, and so should be removed if possible,
// but existing code in web/ breaks some of these settinsg
"noImplicitThis": false,
"noImplicitReturns": false,
"noImplicitAny": false,
"strictFunctionTypes": false,
"noUnusedLocals": false,
"allowJs": true,
"allowSyntheticDefaultImports": true,
"baseUrl": "./",
"inlineSources": true,
"lib": ["es6", "dom"],
"module": "es6",
"moduleResolution": "Node16",
"outDir": "../build/obj",
"rootDir": "./",
"sourceMap": true,

View file

@ -1,5 +1,5 @@
{
"extends": "../../../../tsconfig.esm-base.json",
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"rootDir": ".",

View file

@ -1,5 +1,5 @@
{
"extends": "../../../tsconfig.esm-base.json",
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "build/src/",

View file

@ -1,5 +1,5 @@
{
"extends": "../../../tsconfig.esm-base.json",
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"composite": true,
"declaration": true,

View file

@ -1,10 +1,8 @@
{
"extends": "../../../tsconfig-base.json",
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"composite": true,
"declaration": true,
"module": "es2020",
"moduleResolution": "node",
"rootDir": ".",
"outDir": "build/",
},

View file

@ -1,5 +1,5 @@
{
"extends": "../../../../../tsconfig.esm-base.json",
"extends": "../../../../../tsconfig.base.json",
"compilerOptions": {
"rootDir": ".",

View file

@ -1,5 +1,5 @@
{
"extends": "../../../tsconfig.esm-base.json",
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "build/src/",

View file

@ -6,7 +6,6 @@
"rootDirs": ["./", "../src/"],
"outDir": "../build/test",
"esModuleInterop": true,
"moduleResolution": "node16",
"allowSyntheticDefaultImports": true,
"baseUrl": ".",
"paths": {

View file

@ -1,5 +1,5 @@
{
"extends": "../../../tsconfig.esm-base.json",
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "build/src/",

View file

@ -1,5 +1,5 @@
{
"extends": "../../../tsconfig.esm-base.json",
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "build/src/",

View file

@ -6,7 +6,6 @@
"rootDirs": ["./", "../src/"],
"outDir": "../build/test",
"esModuleInterop": true,
"moduleResolution": "node16",
"allowSyntheticDefaultImports": true,
"baseUrl": ".",
"paths": {

View file

@ -1,5 +1,5 @@
import { assert } from "chai";
import defaultWordBreaker from './wordbreakers/default-wordbreaker-esm.js';
import defaultWordBreaker from '@keymanapp/models-wordbreakers';
import {decorateWithJoin} from '../src/join-word-breaker-decorator.js';
describe('The join word breaker decorator', function () {

View file

@ -1,5 +1,5 @@
import { assert } from "chai";
import defaultWordBreaker from './wordbreakers/default-wordbreaker-esm.js';
import defaultWordBreaker from '@keymanapp/models-wordbreakers';
import {decorateWithScriptOverrides} from '../src/script-overrides-decorator.js';
const THIN_SPACE = "\u2009";

View file

@ -6,7 +6,6 @@
"rootDirs": ["./", "../src/"],
"outDir": "../build/test",
"esModuleInterop": true,
"moduleResolution": "node16",
"allowSyntheticDefaultImports": true,
"baseUrl": ".",
"paths": {

View file

@ -1,3 +0,0 @@
Wordbreakers ES Module format
TODO: once we move common/models/wordbreakers to ESM, eliminate this.

File diff suppressed because it is too large Load diff

View file

@ -1,383 +0,0 @@
// TEMP: esm version of /common/models/wordbreakers/default/index.ts
import { I, WORD_BREAK_PROPERTY, WordBreakProperty } from './data.js';
/**
* Word breaker based on Unicode Standard Annex #29, Section 4.1:
* Default Word Boundary Specification.
*
* @see http://unicode.org/reports/tr29/#Word_Boundaries
* @see https://github.com/eddieantonio/unicode-default-word-boundary/tree/v12.0.0
*/
export default function default_(text: string): Span[] {
let boundaries = findBoundaries(text);
if (boundaries.length == 0) {
return [];
}
// All non-empty strings have at least TWO boundaries: at the start and at the end of
// the string.
let spans = [];
for (let i = 0; i < boundaries.length - 1; i++) {
let start = boundaries[i];
let end = boundaries[i + 1];
let span = new LazySpan(text, start, end);
if (isNonSpace(span.text)) {
spans.push(span);
// Preserve a sequence-final space if it exists. Needed to signal "end of word".
} else if (i == boundaries.length - 2) { // if "we just checked the final boundary"...
// We don't want to return the whitespace itself; the correct token is simply ''.
span = new LazySpan(text, end, end);
spans.push(span);
}
}
return spans;
}
// Utilities //
// type WordBreakProperty = data.WordBreakProperty;
// const WordBreakProperty = data.WordBreakProperty;
// type I = data.I;
// const I = data.I;
// const WORD_BREAK_PROPERTY = data.WORD_BREAK_PROPERTY;
/**
* A span that does not cut out the substring until it absolutely has to!
*/
class LazySpan implements Span {
private _source: string;
readonly start: number;
readonly end: number;
constructor(source: string, start: number, end: number) {
this._source = source;
this.start = start;
this.end = end;
}
get text(): string {
return this._source.substring(this.start, this.end);
}
get length(): number {
return this.end - this.start;
}
}
/**
* Returns true when the chunk does not solely consist of whitespace.
*
* @param chunk a chunk of text. Starts and ends at word boundaries.
*/
function isNonSpace(chunk: string): boolean {
return !Array.from(chunk).map(property).every(wb => (
wb === WordBreakProperty.CR ||
wb === WordBreakProperty.LF ||
wb === WordBreakProperty.Newline ||
wb === WordBreakProperty.WSegSpace
));
}
/**
* Yields a series of string indices where a word break should
* occur. That is, there should be a break BEFORE each string
* index yielded by this generator.
*
* @param text Text to find word boundaries in.
*/
function findBoundaries(text: string): number[] {
// WB1 and WB2: no boundaries if given an empty string.
if (text.length === 0) {
// There are no boundaries in an empty string!
return [];
}
// This algorithm works by maintaining a sliding window of four SCALAR VALUES.
//
// - Scalar values? JavaScript strings are NOT actually a string of
// Unicode code points; some characters are made up of TWO
// JavaScript indices. e.g.,
// "💩".length === 2;
// "💩"[0] === '\uD83D';
// "💩"[1] === '\uDCA9';
//
// These characters that are represented by TWO indices are
// called "surrogate pairs". Since we don't want to be in the
// "middle" of a character, make sure we're always advancing
// by scalar values, and NOT indices. That means, we sometimes
// need to advance by TWO indices, not just one.
// - Four values? Some rules look at what's to the left of
// left, and some look at what's to the right of right. So
// keep track of this!
let boundaries = [];
let rightPos: number;
let lookaheadPos = 0; // lookahead, one scalar value to the right of right.
// Before the start of the string is also the start of the string.
let lookbehind: WordBreakProperty;
let left = WordBreakProperty.sot;
let right = WordBreakProperty.sot;
let lookahead = wordbreakPropertyAt(0);
// Count RIs to make sure we're not splitting emoji flags:
let nConsecutiveRegionalIndicators = 0;
do {
// Shift all positions, one scalar value to the right.
rightPos = lookaheadPos;
lookaheadPos = positionAfter(lookaheadPos);
// Shift all properties, one scalar value to the right.
[lookbehind, left, right, lookahead] =
[left, right, lookahead, wordbreakPropertyAt(lookaheadPos)];
// Break at the start and end of text, unless the text is empty.
// WB1: Break at start of text...
if (left === WordBreakProperty.sot) {
boundaries.push(rightPos);
continue;
}
// WB2: Break at the end of text...
if (right === WordBreakProperty.eot) {
boundaries.push(rightPos);
break; // Reached the end of the string. We're done!
}
// WB3: Do not break within CRLF:
if (left === WordBreakProperty.CR && right === WordBreakProperty.LF)
continue;
// WB3b: Otherwise, break after...
if (left === WordBreakProperty.Newline ||
left === WordBreakProperty.CR ||
left === WordBreakProperty.LF) {
boundaries.push(rightPos);
continue;
}
// WB3a: ...and before newlines
if (right === WordBreakProperty.Newline ||
right === WordBreakProperty.CR ||
right === WordBreakProperty.LF) {
boundaries.push(rightPos);
continue;
}
// TODO: WB3c is not implemented, due to its complex, error-prone
// implementation, requiring a ginormous regexp, and the fact that
// the only thing it does is prevent big emoji sequences from being
// split up, like 🧚🏼‍♂️
// https://www.unicode.org/Public/emoji/12.0/emoji-zwj-sequences.txt
// WB3d: Keep horizontal whitespace together
if (left === WordBreakProperty.WSegSpace && right == WordBreakProperty.WSegSpace)
continue;
// WB4: Ignore format and extend characters
// This is to keep grapheme clusters together!
// See: Section 6.2: https://unicode.org/reports/tr29/#Grapheme_Cluster_and_Format_Rules
// N.B.: The rule about "except after sot, CR, LF, and
// Newline" already been by WB1, WB2, WB3a, and WB3b above.
while (right === WordBreakProperty.Format ||
right === WordBreakProperty.Extend ||
right === WordBreakProperty.ZWJ) {
// Continue advancing in the string, as if these
// characters do not exist. DO NOT update left and
// lookbehind however!
[rightPos, lookaheadPos] = [lookaheadPos, positionAfter(lookaheadPos)];
[right, lookahead] = [lookahead, wordbreakPropertyAt(lookaheadPos)];
}
// In ignoring the characters in the previous loop, we could
// have fallen off the end of the string, so end the loop
// prematurely if that happens!
if (right === WordBreakProperty.eot) {
boundaries.push(rightPos);
break;
}
// WB4 (continued): Lookahead must ALSO ignore these format,
// extend, ZWJ characters!
while (lookahead === WordBreakProperty.Format ||
lookahead === WordBreakProperty.Extend ||
lookahead === WordBreakProperty.ZWJ) {
// Continue advancing in the string, as if these
// characters do not exist. DO NOT update left and right,
// however!
lookaheadPos = positionAfter(lookaheadPos);
lookahead = wordbreakPropertyAt(lookaheadPos);
}
// WB5: Do not break between most letters.
if (isAHLetter(left) && isAHLetter(right))
continue;
// Do not break across certain punctuation
// WB6: (Don't break before apostrophes in contractions)
if (isAHLetter(left) && isAHLetter(lookahead) &&
(right === WordBreakProperty.MidLetter || isMidNumLetQ(right)))
continue;
// WB7: (Don't break after apostrophes in contractions)
if (isAHLetter(lookbehind) && isAHLetter(right) &&
(left === WordBreakProperty.MidLetter || isMidNumLetQ(left)))
continue;
// WB7a
if (left === WordBreakProperty.Hebrew_Letter && right === WordBreakProperty.Single_Quote)
continue;
// WB7b
if (left === WordBreakProperty.Hebrew_Letter && right === WordBreakProperty.Double_Quote &&
lookahead === WordBreakProperty.Hebrew_Letter)
continue;
// WB7c
if (lookbehind === WordBreakProperty.Hebrew_Letter && left === WordBreakProperty.Double_Quote &&
right === WordBreakProperty.Hebrew_Letter)
continue;
// Do not break within sequences of digits, or digits adjacent to letters.
// e.g., "3a" or "A3"
// WB8
if (left === WordBreakProperty.Numeric && right === WordBreakProperty.Numeric)
continue;
// WB9
if (isAHLetter(left) && right === WordBreakProperty.Numeric)
continue;
// WB10
if (left === WordBreakProperty.Numeric && isAHLetter(right))
continue;
// Do not break within sequences, such as 3.2, 3,456.789
// WB11
if (lookbehind === WordBreakProperty.Numeric && right === WordBreakProperty.Numeric &&
(left === WordBreakProperty.MidNum || isMidNumLetQ(left)))
continue;
// WB12
if (left === WordBreakProperty.Numeric && lookahead === WordBreakProperty.Numeric &&
(right === WordBreakProperty.MidNum || isMidNumLetQ(right)))
continue;
// WB13: Do not break between Katakana
if (left === WordBreakProperty.Katakana && right === WordBreakProperty.Katakana)
continue;
// Do not break from extenders (e.g., U+202F NARROW NO-BREAK SPACE)
// WB13a
if ((isAHLetter(left) ||
left === WordBreakProperty.Numeric ||
left === WordBreakProperty.Katakana ||
left === WordBreakProperty.ExtendNumLet) &&
right === WordBreakProperty.ExtendNumLet)
continue;
// WB13b
if ((isAHLetter(right) ||
right === WordBreakProperty.Numeric ||
right === WordBreakProperty.Katakana) && left === WordBreakProperty.ExtendNumLet)
continue;
// WB15 & WB16:
// Do not break within emoji flag sequences. That is, do not break between
// regional indicator (RI) symbols if there is an odd number of RI
// characters before the break point.
if (right === WordBreakProperty.Regional_Indicator) {
// Emoji flags are actually composed of TWO scalar values, each being a
// "regional indicator". These indicators correspond to Latin letters. Put
// two of them together, and they spell out an ISO 3166-1-alpha-2 country
// code. Since these always come in pairs, NEVER split the pairs! So, if
// we happen to be inside the middle of an odd numbered of
// Regional_Indicators, DON'T SPLIT IT!
nConsecutiveRegionalIndicators += 1;
if ((nConsecutiveRegionalIndicators % 2) == 1) {
continue;
}
} else {
nConsecutiveRegionalIndicators = 0;
}
// WB999: Otherwise, break EVERYWHERE (including around ideographs)
boundaries.push(rightPos);
} while (rightPos < text.length);
return boundaries;
///// Internal utility functions /////
/**
* Returns the position of the start of the next scalar value. This jumps
* over surrogate pairs.
*
* If asked for the character AFTER the end of the string, this always
* returns the length of the string.
*/
function positionAfter(pos: number): number {
if (pos >= text.length) {
return text.length;
} else if (isStartOfSurrogatePair(text[pos])) {
return pos + 2;
}
return pos + 1;
}
/**
* Return the value of the Word_Break property at the given string index.
* @param pos position in the text.
*/
function wordbreakPropertyAt(pos: number) {
if (pos < 0) {
return WordBreakProperty.sot; // Always "start of string" before the string starts!
} else if (pos >= text.length) {
return WordBreakProperty.eot; // Always "end of string" after the string ends!
} else if (isStartOfSurrogatePair(text[pos])) {
// Surrogate pairs the next TWO items from the string!
return property(text[pos] + text[pos + 1]);
}
return property(text[pos]);
}
// Word_Break rule macros
// See: https://unicode.org/reports/tr29/#WB_Rule_Macros
function isAHLetter(prop: WordBreakProperty): boolean {
return prop === WordBreakProperty.ALetter ||
prop === WordBreakProperty.Hebrew_Letter;
}
function isMidNumLetQ(prop: WordBreakProperty): boolean {
return prop === WordBreakProperty.MidNumLet ||
prop === WordBreakProperty.Single_Quote;
}
}
function isStartOfSurrogatePair(character: string) {
let codeUnit = character.charCodeAt(0);
return codeUnit >= 0xD800 && codeUnit <= 0xDBFF;
}
/**
* Return the Word_Break property value for a character.
* Note that
* @param character a scalar value
*/
function property(character: string): WordBreakProperty {
// This MUST be a scalar value.
// TODO: remove dependence on character.codepointAt()?
let codepoint = character.codePointAt(0) as number;
return searchForProperty(codepoint, 0, WORD_BREAK_PROPERTY.length - 1);
}
/**
* Binary search for the word break property of a given CODE POINT.
*
* The auto-generated data.ts master array defines a **character range**
* lookup table. If a character's codepoint is equal to or greater than
* the I.Start value for an entry and exclusively less than the next entry,
* it falls in the first entry's range bucket and is classified accordingly
* by this method.
*/
function searchForProperty(codePoint: number, left: number, right: number): WordBreakProperty {
// All items that are not found in the array are assigned the 'Other' property.
if (right < left) {
return WordBreakProperty.Other;
}
let midpoint = left + ~~((right - left) / 2);
let candidate = WORD_BREAK_PROPERTY[midpoint];
let nextRange = WORD_BREAK_PROPERTY[midpoint + 1];
let startOfNextRange = nextRange ? nextRange[I.Start] : Infinity;
if (codePoint < candidate[I.Start]) {
return searchForProperty(codePoint, left, midpoint - 1);
} else if (codePoint >= startOfNextRange) {
return searchForProperty(codePoint, midpoint + 1, right);
}
// We found it!
return candidate[I.Value];
}

View file

@ -1,3 +1,3 @@
{
"extends": "../../../tsconfig.esm-base.json",
"extends": "../../../tsconfig.base.json",
}

View file

@ -1,5 +1,5 @@
{
"extends": "../../../../tsconfig.esm-base.json",
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"rootDir": ".",

View file

@ -1,5 +1,5 @@
{
"extends": "../../../tsconfig.esm-base.json",
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"target": "es2022",

View file

@ -1,5 +1,5 @@
{
"extends": "../../../tsconfig.esm-base.json",
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"rootDir": ".",
"outDir": "build/",

View file

@ -1,5 +1,18 @@
{
"compilerOptions": {
"module": "ES2022",
"target": "es2022",
"moduleResolution": "node16",
"forceConsistentCasingInFileNames": true,
"sourceMap": true,
"alwaysStrict": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"noImplicitAny": true,
"strictBindCallApply": true,
"strictFunctionTypes": true,
"noUnusedLocals": true,
"rootDir": ".",
// TODO: move all compiler options here
@ -9,7 +22,9 @@
"declarationMap": true,
"baseUrl": ".",
"paths": {
"@keymanapp/common-types": ["./common/web/types/src/main"],
"@keymanapp/input-processor": ["./common/web/input-processor/src"],
"@keymanapp/keyboard-processor": ["./common/web/keyboard-processor/src"],
"@keymanapp/keyman": ["./web" ],

View file

@ -1,12 +0,0 @@
{
// Lists only CommonJS or 'none' modules; as we move modules from cjs/none to
// esm, we should move them from here into tsconfig.esm.json. Eventually, when
// we have only ES modules, we'll delete this file.
"files": [],
"include": [],
"references": [
{ "path": "./common/predictive-text/testing/one-stage-embedded-webworker/tsconfig.json" },
{ "path": "./common/predictive-text/testing/two-stage-embedded-webworker/tsconfig.json" },
{ "path": "./common/predictive-text/testing/two-stage-embedded-webworker/worker/tsconfig.json" },
]
}

View file

@ -1,24 +0,0 @@
{
"extends": "./tsconfig-base.json",
"compilerOptions": {
"module": "ES2022",
"target": "es2022",
"moduleResolution": "Node16",
"forceConsistentCasingInFileNames": true,
"sourceMap": true,
"alwaysStrict": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"noImplicitAny": true,
"strictBindCallApply": true,
"strictFunctionTypes": true,
"noUnusedLocals": true,
"paths": {
"@keymanapp/keyman-version": ["./common/web/keyman-version/keyman-version.mts"],
"@keymanapp/common-types": ["./common/web/types/src/main"],
// "@keymanapp/": ["core/include/ldml/ldml-keyboard-constants"],
},
},
}

View file

@ -1,19 +1,28 @@
{
// Lists only ES modules; as we move modules from cjs/none to esm, we should
// move them from tsconfig.cjs.json to here. Eventually, when we have only ES
// modules, we can rename this to tsconfig.json.
"files": [],
"include": [],
"references": [
{ "path": "./core/include/ldml/tsconfig.json" },
//{ "path": "./developer/src/kmc/test/tsconfig.json" },
{ "path": "./common/models/templates/tsconfig.json" },
{ "path": "./common/models/types/tsconfig.json" },
{ "path": "./common/models/wordbreakers/tsconfig.json" },
{ "path": "./common/predictive-text/tsconfig.json" },
{ "path": "./common/tools/hextobin/" },
{ "path": "./common/web/input-processor/tsconfig.json" },
{ "path": "./common/web/keyboard-processor/tsconfig.json" },
{ "path": "./common/web/keyman-version" },
{ "path": "./common/web/lm-message-types/" },
{ "path": "./common/web/lm-worker/" },
{ "path": "./common/web/recorder/tsconfig.json" },
{ "path": "./common/web/sentry-manager/src/tsconfig.json" },
{ "path": "./common/web/types/" },
{ "path": "./common/web/utils/tsconfig.json" },
{ "path": "./developer/src/common/web/test-helpers/tsconfig.json" },
{ "path": "./developer/src/kmc/tsconfig.json" },
// { "path": "./developer/src/kmc-analyze/test/tsconfig.json" },
{ "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-kmn/test/tsconfig.json" },
{ "path": "./developer/src/kmc-kmn/tsconfig.json" },
{ "path": "./developer/src/kmc-keyboard-info/test/tsconfig.json" },
@ -28,27 +37,10 @@
{ "path": "./developer/src/kmc-package/tsconfig.json" },
{ "path": "./developer/src/server/tsconfig.json" },
{ "path": "./common/web/keyman-version" },
{ "path": "./common/web/types/" },
{ "path": "./common/web/input-processor/tsconfig.json" },
{ "path": "./common/web/keyboard-processor/tsconfig.json" },
{ "path": "./common/web/recorder/tsconfig.json" },
{ "path": "./common/web/sentry-manager/src/tsconfig.json" },
{ "path": "./common/web/utils/tsconfig.json" },
{ "path": "./common/models/templates/tsconfig.json" },
{ "path": "./common/models/types/tsconfig.json" },
{ "path": "./common/models/wordbreakers/tsconfig.json" },
{ "path": "./common/predictive-text/tsconfig.json" },
{ "path": "./resources/build/version/" },
{ "path": "./web/src/tsconfig.all.json" },
// { "path": "./web/tools/recorder/tsconfig.json" },
// { "path": "./web/tools/sourcemap-root/tsconfig.json" },
{ "path": "./common/web/lm-message-types/" },
{ "path": "./common/web/lm-worker/" },
{ "path": "./common/tools/hextobin/" },
{ "path": "./resources/build/version/" },
]
}

View file

@ -1,15 +1,24 @@
{
"extends": "../tsconfig-base.json",
"extends": "../tsconfig.base.json",
// TODO: eliminate settings duplicated in ../tsconfig.base.json
"compilerOptions": {
// Primary settings - the version of ES6 we can target in TS, our downcompile target,
// and our module-related settings.
"allowSyntheticDefaultImports": true,
"lib": ["es6"],
"module": "es6",
"moduleResolution": "Node16",
"target": "es5",
// TODO: These override ../tsconfig.base.json settings, and so should be removed if possible,
// but existing code in web/ breaks some of these settinsg
"noImplicitThis": false,
"noImplicitReturns": false,
"noImplicitAny": false,
"strictFunctionTypes": false,
"noUnusedLocals": false,
// Other settings - declaration files, sourcemapping, and other miscellaneous bits.
"allowJs": false,
"declaration": true,