Skip to content

Commit

Permalink
Merge pull request #114 from kwonoj/feat-locatebinary
Browse files Browse the repository at this point in the history
feat(loadmodule): expose locatebinary
  • Loading branch information
kwonoj authored Oct 24, 2018
2 parents cca9ed2 + 867e572 commit c1fe98c
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 40 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
<a name="2.0.0-beta.3"></a>
# [2.0.0-beta.3](https://github.com/kwonoj/cld3-asm/compare/v2.0.0-beta.2...v2.0.0-beta.3) (2018-10-24)


### Features

* **loadmodule:** expose locatebinary ([606ff1b](https://github.com/kwonoj/cld3-asm/commit/606ff1b))



<a name="2.0.0-beta.2"></a>
# [2.0.0-beta.2](https://github.com/kwonoj/cld3-asm/compare/v2.0.0-beta.1...v2.0.0-beta.2) (2018-10-23)

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ const cldFactory = await loadModule();
`loadModule` loads wasm binary, initialize it, and returns factory function to create instance of cld3 [language identifier.](https://github.com/kwonoj/cld3-asm/blob/1a86bb67abcebc2cd0e90a83149292eb044e4122/src/cldAsmModule.ts#L70-L97)

```js
loadModule(environment?: ENVIRONMENT): Promise<CldFactory>
loadModule({timeout?: number, locateBinary?: (wasmPath: string) => string | object}): Promise<CldFactory>
```

It accepts `environment` as option, allow to override running environment and ignores internal runtime detection. This is mostly for [Electron](https://electron.atom.io/)'s renderer process where node.js and `fetch` are available both, to selectively opt-in which way to use. It is important to note `loadModule` doesn't interop incorrect option value matching, like try to load correct binary when supply endpoint to file path with set env to browser.
It allows to specify timeout to wait until wasm binary compliation & load, also allows to override to lookup binary of wasm. Based on environment & bundling configurations, it is not sufficient to rely on default resolution logic. `locateBinary` expects to return path of binary (i.e remote endpoint url) or loader-specific object if it's bundled by bundler. Check [examples](https://github.com/kwonoj/cld3-asm/tree/master/examples) for usecases.

## Creating language identifier

Expand Down
5 changes: 3 additions & 2 deletions examples/browser_remote/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//tslint:disable:no-console
//tslint:disable:no-console no-require-imports
import { loadModule } from '../../src/index';
import { enableLogger } from '../../src/util/logger';
import { runCld } from '../runCld';
Expand All @@ -7,7 +7,8 @@ enableLogger(console.log.bind(console));

const runBrowserCld = async () => {
const cldFactory = await loadModule({
binaryRemoteEndpoint: `http://localhost:8888`
//let file-loader resolves wasm binary when bundling
locateBinary: (_wasmPath: string) => require('../../src/lib/cld3_web.wasm')
});

runCld(cldFactory);
Expand Down
5 changes: 1 addition & 4 deletions examples/browser_remote/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,7 @@ module.exports = {
{
test: /.wasm$/,
type: 'javascript/auto',
loader: 'file-loader',
options: {
name: '[name].[ext]'
}
loader: 'file-loader'
}
]
},
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cld3-asm",
"version": "2.0.0-beta.2",
"version": "2.0.0-beta.3",
"description": "WebAssembly based Javascript bindings for google compact language detector 3",
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
Expand Down
45 changes: 32 additions & 13 deletions spec/cld-asm/loadModule-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,13 @@ jest.mock('../../src/cldLoader');
jest.mock('emscripten-wasm-loader', () => ({
isWasmEnabled: jest.fn(),
isNode: jest.fn(),
getModuleLoader: jest.fn(),
ENVIRONMENT: {
WEB: 'WEB',
NODE: 'NODE'
}
getModuleLoader: jest.fn()
}));

describe('loadModule', () => {
it('should create moduleLoader on browser', async () => {
const mockModuleLoader = jest.fn();
(isNode as jest.Mock).mockReturnValueOnce(false);
(isNode as jest.Mock).mockReturnValue(false);

(getModuleLoaderMock as jest.Mock).mockImplementationOnce((cb: Function) => {
cb();
Expand All @@ -35,28 +31,51 @@ describe('loadModule', () => {

it('should create module on node', async () => {
const mockModuleLoader = jest.fn();
(isNode as jest.Mock).mockReturnValueOnce(true);
(isNode as jest.Mock).mockReturnValue(true);

(getModuleLoaderMock as jest.Mock).mockReturnValueOnce(mockModuleLoader);
await loadModule();

expect((getModuleLoaderMock as jest.Mock).mock.calls[0][1]).to.equal(nodecld3Mock);
});

it('should use remoteEndpoint on browser', async () => {
it('should use lookupBinary on browser', async () => {
const mockModuleLoader = jest.fn();
(isNode as jest.Mock).mockReturnValueOnce(false);
(isNode as jest.Mock).mockReturnValue(false);

(getModuleLoaderMock as jest.Mock).mockReturnValueOnce(mockModuleLoader);
await loadModule({ binaryRemoteEndpoint: 'dummy' });
await loadModule({ locateBinary: () => 'dummy' });

expect((getModuleLoaderMock as jest.Mock).mock.calls[0][2].locateFile('test.wasm')).to.equal('dummy');
});

it('should use lookupBinary on node', async () => {
const mockModuleLoader = jest.fn();
(isNode as jest.Mock).mockReturnValue(true);

(getModuleLoaderMock as jest.Mock).mockReturnValueOnce(mockModuleLoader);
await loadModule({ locateBinary: () => 'dummy' });

const { locateFile } = (getModuleLoaderMock as jest.Mock).mock.calls[0][2];
expect(locateFile('test.wasm')).to.equal('dummy');
});

it('should not override path for wasm binary on node', async () => {
const mockModuleLoader = jest.fn();
(isNode as jest.Mock).mockReturnValue(true);

(getModuleLoaderMock as jest.Mock).mockImplementationOnce((cb: Function) => {
cb();
return mockModuleLoader;
});
await loadModule();

expect((getModuleLoaderMock as jest.Mock).mock.calls[0][2]).to.be.undefined;
expect((getModuleLoaderMock as jest.Mock).mock.calls[0][3]).to.deep.equal({ binaryRemoteEndpoint: 'dummy' });
});

it('should override path for wasm binary on browser without remote endpoint', async () => {
it('should override path for wasm binary on browser', async () => {
const mockModuleLoader = jest.fn();
(isNode as jest.Mock).mockReturnValueOnce(false);
(isNode as jest.Mock).mockReturnValue(false);

(getModuleLoaderMock as jest.Mock).mockImplementationOnce((cb: Function) => {
cb();
Expand Down
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export { loadModule } from './loadModule';
export { log, enableLogger } from './util/logger';
export { ENVIRONMENT } from 'emscripten-wasm-loader';
export { LanguageIdentifier, CldFactory } from './cldFactory';
export { LanguageResult } from './cldAsmModule';
export { LanguageCode } from './languageCode';
38 changes: 22 additions & 16 deletions src/loadModule.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { getModuleLoader, isNode } from 'emscripten-wasm-loader';
import { ModuleInitOption } from 'emscripten-wasm-loader/dist/types/getModuleLoader';
import { CldAsmModule } from './cldAsmModule';
import { CldFactory } from './cldFactory';
import { cldLoader } from './cldLoader';
Expand All @@ -8,34 +7,41 @@ import { log } from './util/logger';
/**
* Load, initialize wasm / asm.js binary to use actual cld wasm instances.
*
* @param {moduleInitOption} [ModuleInitOption] additional option to configure module loader
* @param [InitOptions] Options to initialize cld3 wasm binary.
* @param {number} [InitOptions.timeout] - timeout to wait wasm binary compilation & load.
* @param {string | object} [InitOptions.locateBinary] - custom resolution logic for wasm binary.
* It could be either remote endpoint url, or loader-returned object for bundler. Check examples/browser_* for references.
*
* @returns {() => Promise<CldFactory>} Function to load module
*/
const loadModule = async (moduleInitOption?: Partial<ModuleInitOption>) => {
const loadModule = async ({
timeout,
locateBinary
}: Partial<{ timeout: number; locateBinary: (filePath: string) => string | object }> = {}) => {
log(`loadModule: loading cld3 module`);

//imports MODULARIZED emscripten preamble
const runtimeModule = isNode() ? require(`./lib/cld3_node`) : require(`./lib/cld3_web`); //tslint:disable-line:no-require-imports no-var-requires

// in Browser environment if remote endpoint is not specified cld3-asm overrides preamble's locateFile to provide wasm binary
// instead of preamble triggers fetch to file:// resource paths.
// Bundler (i.e webpack) should configure proper loader settings for this.
const isPathOverrideRequired = !isNode() && (!moduleInitOption || !moduleInitOption.binaryRemoteEndpoint);
const overriddenModule = !isPathOverrideRequired
? undefined
: {
locateFile: (filePath: string) =>
filePath.endsWith('.wasm')
? require('./lib/cld3_web.wasm') //tslint:disable-line:no-require-imports no-var-requires
: filePath
};
//tslint:disable-next-line:no-require-imports no-var-requires
const lookupBinary = locateBinary || ((_filePath: string) => require('./lib/cld3_web.wasm'));

//Build module object to construct wasm binary module via emscripten preamble.
//This allows to override default wasm binary resolution in preamble.
//By default, cld3-asm overrides to direct require to binary on *browser* environment to allow bundler like webpack resolves it.
//On node, it relies on default resolution logic.
const overriddenModule =
isNode() && !locateBinary
? undefined
: {
locateFile: (filePath: string) => (filePath.endsWith('.wasm') ? lookupBinary(filePath) : filePath)
};

const moduleLoader = await getModuleLoader<CldFactory, CldAsmModule>(
(runtime: CldAsmModule) => cldLoader(runtime),
runtimeModule,
overriddenModule,
moduleInitOption
{ timeout }
);

return moduleLoader();
Expand Down

0 comments on commit c1fe98c

Please sign in to comment.