diff --git a/CHANGELOG.md b/CHANGELOG.md index 4949124..d067ac9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ + +# [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)) + + + # [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) diff --git a/README.md b/README.md index 04c8658..88a9ea2 100644 --- a/README.md +++ b/README.md @@ -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 +loadModule({timeout?: number, locateBinary?: (wasmPath: string) => string | object}): Promise ``` -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 diff --git a/examples/browser_remote/index.ts b/examples/browser_remote/index.ts index cab82d4..49e1880 100644 --- a/examples/browser_remote/index.ts +++ b/examples/browser_remote/index.ts @@ -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'; @@ -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); diff --git a/examples/browser_remote/webpack.config.js b/examples/browser_remote/webpack.config.js index 483828b..413d5aa 100644 --- a/examples/browser_remote/webpack.config.js +++ b/examples/browser_remote/webpack.config.js @@ -32,10 +32,7 @@ module.exports = { { test: /.wasm$/, type: 'javascript/auto', - loader: 'file-loader', - options: { - name: '[name].[ext]' - } + loader: 'file-loader' } ] }, diff --git a/package-lock.json b/package-lock.json index c1a0fca..3fe1085 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cld3-asm", - "version": "2.0.0-beta.2", + "version": "2.0.0-beta.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index a26a84d..ca5cd4f 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/spec/cld-asm/loadModule-spec.ts b/spec/cld-asm/loadModule-spec.ts index 5f2b225..609308e 100644 --- a/spec/cld-asm/loadModule-spec.ts +++ b/spec/cld-asm/loadModule-spec.ts @@ -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(); @@ -35,7 +31,7 @@ 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(); @@ -43,20 +39,43 @@ describe('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(); diff --git a/src/index.ts b/src/index.ts index bef2c87..211c627 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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'; diff --git a/src/loadModule.ts b/src/loadModule.ts index 3f1f446..8111f5d 100644 --- a/src/loadModule.ts +++ b/src/loadModule.ts @@ -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'; @@ -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} Function to load module */ -const loadModule = async (moduleInitOption?: Partial) => { +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( (runtime: CldAsmModule) => cldLoader(runtime), runtimeModule, overriddenModule, - moduleInitOption + { timeout } ); return moduleLoader();