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

Remove web worker replacement #976

Merged
merged 5 commits into from
Feb 10, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
26 changes: 8 additions & 18 deletions build/readme.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,31 @@
# Build Scripts
This folder holds common build scripts accessed via the various `npm run` commands.

Codegen is executed when calling `npm install` in order to generate all artifacts needed for the build to pass
## Bundeling all the code

The bundeling process can be split into several steps:

`npx run tsc`
This command will transpile all the typescript files into javascript files and place them in the `rollup/build` folder.

`npm run build-glsl`
This command will copy all the shader files to the build output and convert the shaders into strings that can be imported to javascript.

`npm run build-css`
This command will compile the css code and create the css file.

`npm run build-prod` or `npm run build-prod-min` or `npm run build-dev`
These commands will use rollup to bundle the code. This is where the magic happens and uses some files in this folder.

`banner.js` is used to create a banner at the beginning of the output file
`banner.ts` is used to create a banner at the beginning of the output file

`rollup_plugins.js` is used to define common plugins for rollup configurations
`rollup_plugins.ts` is used to define common plugins for rollup configurations

`rollup_plugin_minify_style_spec.js` is used to specify the plugin used in style spec bundeling
`rollup_plugin_minify_style_spec.ts` is used to specify the plugin used in style spec bundeling

In the `rollup` folder there are some files that are used as linking files as they link to other files for rollup to pick when bundling.
Rollup also has a configuration in the `package.json` file to signal which files it needs to replace when bundling for the browser, this is where `web_worker_replacement.js` is used - as it replaces the node mocking of web worker that is present in the source code.

Rollup is generating 3 files throughout the process of bundling:

`index.js` a file containing all the code that will run in the main thread.
`index.ts` a file containing all the code that will run in the main thread.

`shared.js` a file containing all the code shared between the main and worker code.
`shared.ts` a file containing all the code shared between the main and worker code.

`worker.js` a file containing all the code the will run in the worker threads.
`worker.ts` a file containing all the code the will run in the worker threads.

These 3 files are then referenced and used by the `bundle_prelude.js` file. It allows loading the web wroker code automatically in web workers without any extra effort from someone who would like to use the library, i.e. it simply works.

Expand All @@ -55,8 +48,5 @@ Generates `style-spec/types.ts` based on the content of `v8.json`. This provides
<hr>

### Generate Release Nodes
The following files are being used to generate release notes:

`release-notes.js` Used to generate release notes when releasing a new version

`release-notes.md.ejs` Used for the generation as a template file
`release-notes.js` Used to generate release notes when releasing a new version
3 changes: 1 addition & 2 deletions build/rollup_plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ import {Plugin} from 'rollup';

export const nodeResolve = resolve({
browser: true,
preferBuiltins: false,
extensions: ['.mjs', '.js', '.json', '.node', '.ts']
preferBuiltins: false
});

export const plugins = (minified: boolean, production: boolean): Plugin[] => [
Expand Down
5 changes: 0 additions & 5 deletions build/web_worker_replacement.ts

This file was deleted.

5 changes: 4 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,8 @@ export default {
transformIgnorePatterns: [
'/node_modules/@mapbox/jsonlint-lines-primitives/lib/jsonlint.js'
],
setupFiles: ['jest-canvas-mock'],
setupFiles: [
'jest-canvas-mock',
'./test/unit/lib/web_worker_mock.ts'
],
};
3 changes: 0 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,6 @@
"ts-node": "^10.4.0",
"typescript": "^4.5.5"
},
"browser": {
"./src/util/web_worker.ts": "./build/web_worker_replacement.ts"
},
"scripts": {
"generate-shaders": "node --loader ts-node/esm --experimental-specifier-resolution=node build/generate-shaders.ts",
"generate-struct-arrays": "node --loader ts-node/esm --experimental-specifier-resolution=node build/generate-struct-arrays.ts",
Expand Down
51 changes: 41 additions & 10 deletions src/util/actor.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,42 @@
import Actor from './actor';
import workerFactory from './web_worker';
import {MessageBus} from '../../test/unit/lib/web_worker_mock';

const originalWorker = global.Worker;

function setTestWorker(MockWorker: { new(...args: any): any}) {
(global as any).Worker = function Worker(_: string) {
const parentListeners = [];
const workerListeners = [];
const parentBus = new MessageBus(workerListeners, parentListeners);
const workerBus = new MessageBus(parentListeners, workerListeners);

parentBus.target = workerBus;
workerBus.target = parentBus;

new MockWorker(workerBus);

return parentBus;
};
}

describe('Actor', () => {
afterAll(() => {
global.Worker = originalWorker;
});

test('forwards responses to correct callback', done => {
jest.spyOn(workerFactory, 'Worker').mockImplementation(function Worker(self) {
this.self = self;
this.actor = new Actor(self, this);
this.test = function (mapId, params, callback) {
setTestWorker(class MockWorker {
self: any;
actor: Actor;
constructor(self) {
this.self = self;
this.actor = new Actor(self, this);
}
test(mapId, params, callback) {
setTimeout(callback, 0, null, params);
};
} as any);
}
});

const worker = workerFactory();

Expand Down Expand Up @@ -38,10 +65,14 @@ describe('Actor', () => {
test('targets worker-initiated messages to correct map instance', done => {
let workerActor;

jest.spyOn(workerFactory, 'Worker').mockImplementation(function Worker(self) {
this.self = self;
this.actor = workerActor = new Actor(self, this);
} as any);
setTestWorker(class MockWorker {
self: any;
actor: Actor;
constructor(self) {
this.self = self;
this.actor = workerActor = new Actor(self, this);
}
});

const worker = workerFactory();

Expand Down
72 changes: 4 additions & 68 deletions src/util/web_worker.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
// When Rollup builds the main bundle this file is replaced with ./build/web_worker_replacement.js
// See package.json 'browser' field and rollup documentation.
// This file is intended for use in the GL-JS test suite when they run on node since node doesn't support workers.
// It implements a MessageBus main thread interface

import MaplibreWorker from '../source/worker';
import maplibregl from '../index';

import type {WorkerSource} from '../source/worker_source';

type MessageListener = (
export type MessageListener = (
a: {
data: any;
target: any;
}
) => unknown;

// The main thread interface. Provided by Worker in a browser environment,
// and MessageBus below in a node environment.
export interface WorkerInterface {
addEventListener(type: 'message', listener: MessageListener): void;
removeEventListener(type: 'message', listener: MessageListener): void;
Expand All @@ -34,64 +28,6 @@ export interface WorkerGlobalScopeInterface {
registerRTLTextPlugin: (_: any) => void;
}

class MessageBus implements WorkerInterface, WorkerGlobalScopeInterface {
addListeners: Array<MessageListener>;
postListeners: Array<MessageListener>;
target: MessageBus;
registerWorkerSource: any;
registerRTLTextPlugin: any;

constructor(addListeners: Array<MessageListener>, postListeners: Array<MessageListener>) {
this.addListeners = addListeners;
this.postListeners = postListeners;
}

addEventListener(event: 'message', callback: MessageListener) {
if (event === 'message') {
this.addListeners.push(callback);
}
}

removeEventListener(event: 'message', callback: MessageListener) {
const i = this.addListeners.indexOf(callback);
if (i >= 0) {
this.addListeners.splice(i, 1);
}
}

postMessage(data: any) {
setTimeout(() => {
try {
for (const listener of this.postListeners) {
listener({data, target: this.target});
}
} catch (e) {
console.error(e);
}
}, 0);
}

terminate() {
this.addListeners.splice(0, this.addListeners.length);
this.postListeners.splice(0, this.postListeners.length);
}

importScripts() { }
}

export default function workerFactory(): WorkerInterface {
const parentListeners = [],
workerListeners = [],
parentBus = new MessageBus(workerListeners, parentListeners),
workerBus = new MessageBus(parentListeners, workerListeners);

parentBus.target = workerBus;
workerBus.target = parentBus;

new workerFactory.Worker(workerBus);

return parentBus;
export default function workerFactory() {
return new Worker(maplibregl.workerUrl);
}

// expose to allow stubbing in unit tests
workerFactory.Worker = MaplibreWorker;
2 changes: 1 addition & 1 deletion test/integration/render/suite_implementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import fs from 'fs';
import path, {dirname} from 'path';
import customLayerImplementations from './custom_layer_implementations';
import {fileURLToPath} from 'url';

import '../../unit/lib/web_worker_mock';
// @ts-ignore
const __dirname = dirname(fileURLToPath(import.meta.url));
let now = 0;
Expand Down
61 changes: 61 additions & 0 deletions test/unit/lib/web_worker_mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import type {WorkerInterface, WorkerGlobalScopeInterface, MessageListener} from '../../../src/util/web_worker';
import MaplibreWorker from '../../../src/source/worker';

export class MessageBus implements WorkerInterface, WorkerGlobalScopeInterface {
addListeners: Array<MessageListener>;
postListeners: Array<MessageListener>;
target: MessageBus;
registerWorkerSource: any;
registerRTLTextPlugin: any;

constructor(addListeners: Array<MessageListener>, postListeners: Array<MessageListener>) {
this.addListeners = addListeners;
this.postListeners = postListeners;
}

addEventListener(event: 'message', callback: MessageListener) {
if (event === 'message') {
this.addListeners.push(callback);
}
}

removeEventListener(event: 'message', callback: MessageListener) {
const i = this.addListeners.indexOf(callback);
if (i >= 0) {
this.addListeners.splice(i, 1);
}
}

postMessage(data: any) {
setTimeout(() => {
try {
for (const listener of this.postListeners) {
listener({data, target: this.target});
}
} catch (e) {
console.error(e);
}
}, 0);
}

terminate() {
this.addListeners.splice(0, this.addListeners.length);
this.postListeners.splice(0, this.postListeners.length);
}

importScripts() { }
}

(global as any).Worker = function Worker(_: string) {
const parentListeners = [];
const workerListeners = [];
const parentBus = new MessageBus(workerListeners, parentListeners);
const workerBus = new MessageBus(parentListeners, workerListeners);

parentBus.target = workerBus;
workerBus.target = parentBus;

new MaplibreWorker(workerBus);

return parentBus;
};