Skip to content

Commit

Permalink
Merge pull request #7800 from keymanapp/change/web/util-modularization
Browse files Browse the repository at this point in the history
change(web): util package modularization, bundling 🧩
  • Loading branch information
jahorton authored Feb 2, 2023
2 parents 8947e5e + 07f43cb commit 84e80fc
Show file tree
Hide file tree
Showing 17 changed files with 1,332 additions and 650 deletions.
36 changes: 36 additions & 0 deletions common/web/keyman-version/build-bundler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Bundles @keymanapp/web-utils as single-file modules based upon the export list in src/index.ts.
*/

import esbuild from 'esbuild';
import { spawn } from 'child_process';

// Bundles to a compact ESModule
esbuild.buildSync({
entryPoints: ['build/version.inc.js'],
bundle: true,
sourcemap: true,
//minify: true, // No need to minify a module.
//keepNames: true,
format: "cjs",
// Sets 'common/web' as a root folder for module resolution;
// this allows the keyman-version import to resolve.
nodePaths: ['..'],
outfile: "build/index.cjs",
tsconfig: 'tsconfig.json',
target: "es5"
});

const dtsBundleCommand = spawn('npx dts-bundle-generator --project tsconfig.json -o build/index.d.ts version.inc.ts', {
shell: true
});

dtsBundleCommand.stdout.on('data', data => console.log(data.toString()));
dtsBundleCommand.stderr.on('data', data => console.error(data.toString()));

// Forces synchronicity; done mostly so that the logs don't get jumbled up.
dtsBundleCommand.on('exit', () => {
if(dtsBundleCommand.exitCode != 0) { // Ensure the exit code gets emitted!
process.exit(dtsBundleCommand.exitCode);
}
});
36 changes: 21 additions & 15 deletions common/web/keyman-version/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -47,28 +47,34 @@ fi
if builder_start_action build; then
# Generate index.ts
echo "
// Generated by common/web/keyman-version/build.sh
namespace com.keyman {
export class KEYMAN_VERSION {
static readonly VERSION = \"$VERSION\";
static readonly VERSION_RELEASE =\"$VERSION_RELEASE\";
static readonly VERSION_MAJOR = \"$VERSION_MAJOR\";
static readonly VERSION_MINOR = \"$VERSION_MINOR\";
static readonly VERSION_PATCH = \"$VERSION_PATCH\";
static readonly TIER =\"$TIER\";
static readonly VERSION_TAG = \"$VERSION_TAG\";
static readonly VERSION_WITH_TAG = \"$VERSION_WITH_TAG\";
static readonly VERSION_ENVIRONMENT = \"$VERSION_ENVIRONMENT\";
static readonly VERSION_GIT_TAG = \"$VERSION_GIT_TAG\";
}
}
// Generated by common/web/keyman-version/build.sh
//
// Note: does not use the 'default' keyword so that the export name is
// correct when converted to a CommonJS module with \`esbuild\`.
export class KEYMAN_VERSION {
static readonly VERSION = \"$VERSION\";
static readonly VERSION_RELEASE =\"$VERSION_RELEASE\";
static readonly VERSION_MAJOR = \"$VERSION_MAJOR\";
static readonly VERSION_MINOR = \"$VERSION_MINOR\";
static readonly VERSION_PATCH = \"$VERSION_PATCH\";
static readonly TIER =\"$TIER\";
static readonly VERSION_TAG = \"$VERSION_TAG\";
static readonly VERSION_WITH_TAG = \"$VERSION_WITH_TAG\";
static readonly VERSION_ENVIRONMENT = \"$VERSION_ENVIRONMENT\";
static readonly VERSION_GIT_TAG = \"$VERSION_GIT_TAG\";
}
// Also provides it as a 'default' export.
export default KEYMAN_VERSION;
" > ./version.inc.ts

# Note: in a dependency build, we'll expect keyman-version to be built by tsc -b
if builder_is_dep_build; then
echo "[$THIS_SCRIPT_IDENTIFIER] skipping tsc -b; will be completed by $builder_dep_parent"
else
npm run build -- $builder_verbose
# Generates a CommonJS variant (in case other modules still need it).
node ./build-bundler.js
fi

builder_finish_action success build
Expand Down
9 changes: 0 additions & 9 deletions common/web/keyman-version/index.ts

This file was deleted.

4 changes: 2 additions & 2 deletions common/web/keyman-version/package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"name": "@keymanapp/keyman-version",
"description": "Keyman global version data",
"main": "./build/index.js",
"exports": "./build/index.js",
"main": "./build/version.inc.js",
"scripts": {
"build": "echo 'Building @keymanapp/keyman-version' && tsc -b",
"clean": "tsc -b --clean"
},
"license": "MIT",
"type": "module",
"devDependencies": {
"typescript": "^4.5.4"
}
Expand Down
6 changes: 3 additions & 3 deletions common/web/keyman-version/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
"compilerOptions": {
"allowJs": false,
"declaration": true,
"module": "none",
"outFile": "./build/index.js",
"module": "es6",
"sourceMap": true,
"outDir": "./build",
"lib": ["es6"],
"target": "es5",
"downlevelIteration": true,
"rootDir": "."
},
"include": [
"index.ts",
"version.inc.ts"
]
}
38 changes: 38 additions & 0 deletions common/web/utils/build-bundler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Bundles @keymanapp/web-utils as single-file modules based upon the export list in src/index.ts.
*/

import esbuild from 'esbuild';
import { spawn } from 'child_process';

// Bundles to a compact ESModule
esbuild.buildSync({
entryPoints: ['build/obj/index.js'],
bundle: true,
sourcemap: true,
//minify: true, // No need to minify a module.
//keepNames: true,
format: "esm",
// Sets 'common/web' as a root folder for module resolution;
// this allows the keyman-version import to resolve.
nodePaths: ['..'],
outfile: "build/lib/index.mjs",
tsconfig: 'tsconfig.json',
target: "es5"
});

// Bundles to a compact CommonJS (classic Node) module
esbuild.buildSync({
entryPoints: ['build/obj/index.js'],
bundle: true,
sourcemap: true,
//minify: true, // No need to minify a module.
//keepNames: true,
format: "cjs",
// Sets 'common/web' as a root folder for module resolution;
// this allows the keyman-version import to resolve.
nodePaths: ['..'],
outfile: "build/lib/index.cjs",
tsconfig: 'tsconfig.json',
target: "es5"
});
4 changes: 4 additions & 0 deletions common/web/utils/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ if builder_start_action build; then
echo "[$THIS_SCRIPT_IDENTIFIER] skipping tsc -b; will be completed by $builder_dep_parent"
else
npm run tsc -- --build "$THIS_SCRIPT_PATH/tsconfig.json"
node build-bundler.js

# So... tsc does declaration-bundling on its own pretty well, at least for local development.
npm run tsc -- --emitDeclarationOnly --outFile ./build/lib/index.d.ts
fi
builder_finish_action success build
fi
4 changes: 4 additions & 0 deletions common/web/utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,9 @@
"@keymanapp/keyman-version": "*",
"@types/node": "^14.0.5",
"typescript": "^4.5.4"
},
"type": "module",
"paths": {
"@keymanapp/keyman-version": "*"
}
}
42 changes: 20 additions & 22 deletions common/web/utils/src/deepCopy.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
namespace com.keyman.utils {
/**
* Function deepCopy
* Scope Private
* @param {Object} p object to copy
* @param {Array=} c0 array member being copied
* @return {Object} clone ('deep copy') of object
* Description Makes an actual copy (not a reference) of an object, copying simple members,
* arrays and member objects but not functions, so use with care!
*/
export function deepCopy<T>(p:T, c0?): T {
var c = c0 || {};
for (var i in p) {
if(typeof p[i] === 'object' && p[i] != null) {
c[i] = (p[i].constructor === Array ) ? [] : {};
deepCopy(p[i],c[i]);
}
else {
c[i] = p[i];
}
/**
* Function deepCopy
* Scope Private
* @param {Object} p object to copy
* @param {Array=} c0 array member being copied
* @return {Object} clone ('deep copy') of object
* Description Makes an actual copy (not a reference) of an object, copying simple members,
* arrays and member objects but not functions, so use with care!
*/
export default function deepCopy<T>(p:T, c0?): T {
var c = c0 || {};
for (var i in p) {
if(typeof p[i] === 'object' && p[i] != null) {
c[i] = (p[i].constructor === Array ) ? [] : {};
deepCopy(p[i],c[i]);
}
else {
c[i] = p[i];
}

return c;
}

return c;
}
107 changes: 55 additions & 52 deletions common/web/utils/src/deviceSpec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,56 @@
namespace com.keyman.utils {
/**
* This class provides an abstract version of com.keyman.Device that is core-friendly,
* containing only the information needed by web-core for text processing use, devoid
* of any direct references to the DOM.
*/
export class DeviceSpec {
readonly browser: DeviceSpec.Browser;
readonly formFactor: DeviceSpec.FormFactor;
readonly OS: DeviceSpec.OperatingSystem;
readonly touchable: boolean;

constructor(browser: string, formFactor: string, OS: string, touchable: boolean) {
switch(browser.toLowerCase() as DeviceSpec.Browser) {
case DeviceSpec.Browser.Chrome:
case DeviceSpec.Browser.Edge:
case DeviceSpec.Browser.Firefox:
case DeviceSpec.Browser.Native:
case DeviceSpec.Browser.Opera:
case DeviceSpec.Browser.Safari:
this.browser = browser.toLowerCase() as DeviceSpec.Browser;
break;
default:
this.browser = DeviceSpec.Browser.Other;
}

switch(formFactor.toLowerCase() as DeviceSpec.FormFactor) {
case DeviceSpec.FormFactor.Desktop:
case DeviceSpec.FormFactor.Phone:
case DeviceSpec.FormFactor.Tablet:
this.formFactor = formFactor.toLowerCase() as DeviceSpec.FormFactor;
break;
default:
throw ("Invalid form factor specified for device: " + formFactor);
}

switch(OS.toLowerCase() as DeviceSpec.OperatingSystem) {
case DeviceSpec.OperatingSystem.Windows.toLowerCase():
case DeviceSpec.OperatingSystem.macOS.toLowerCase():
case DeviceSpec.OperatingSystem.Linux.toLowerCase():
case DeviceSpec.OperatingSystem.Android.toLowerCase():
case DeviceSpec.OperatingSystem.iOS.toLowerCase():
this.OS = OS.toLowerCase() as DeviceSpec.OperatingSystem;
break;
default:
this.OS = DeviceSpec.OperatingSystem.Other;
}

this.touchable = touchable;
}
}

// Namespaces these under DeviceSpec, as each is primarily used with it.
export namespace DeviceSpec {
export enum Browser {
Chrome = 'chrome',
Edge = 'edge',
Expand All @@ -23,55 +75,6 @@ namespace com.keyman.utils {
Phone = 'phone',
Tablet = 'tablet'
}
}

/**
* This class provides an abstract version of com.keyman.Device that is core-friendly,
* containing only the information needed by web-core for text processing use, devoid
* of any direct references to the DOM.
*/
export class DeviceSpec {
readonly browser: Browser;
readonly formFactor: FormFactor;
readonly OS: OperatingSystem;
readonly touchable: boolean;

constructor(browser: string, formFactor: string, OS: string, touchable: boolean) {
switch(browser.toLowerCase() as Browser) {
case Browser.Chrome:
case Browser.Edge:
case Browser.Firefox:
case Browser.Native:
case Browser.Opera:
case Browser.Safari:
this.browser = browser.toLowerCase() as Browser;
break;
default:
this.browser = Browser.Other;
}

switch(formFactor.toLowerCase() as FormFactor) {
case FormFactor.Desktop:
case FormFactor.Phone:
case FormFactor.Tablet:
this.formFactor = formFactor.toLowerCase() as FormFactor;
break;
default:
throw ("Invalid form factor specified for device: " + formFactor);
}

switch(OS.toLowerCase() as OperatingSystem) {
case OperatingSystem.Windows.toLowerCase():
case OperatingSystem.macOS.toLowerCase():
case OperatingSystem.Linux.toLowerCase():
case OperatingSystem.Android.toLowerCase():
case OperatingSystem.iOS.toLowerCase():
this.OS = OS.toLowerCase() as OperatingSystem;
break;
default:
this.OS = OperatingSystem.Other;
}

this.touchable = touchable;
}
}
}
export default DeviceSpec;
Loading

0 comments on commit 84e80fc

Please sign in to comment.