Skip to content

Commit 44dae36

Browse files
author
Anton Eriksson
committed
feat: Create npm package with Infomap worker, changelog and parameters
1 parent 3a2509d commit 44dae36

File tree

11 files changed

+6413
-650
lines changed

11 files changed

+6413
-650
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,6 @@ tmp
3535
__pycache__
3636
.DS_Store
3737
node_modules
38+
dist
39+
interfaces/js/src/worker
3840

Makefile

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,26 +55,32 @@ debug: clean Infomap
5555
# JavaScript through Emscripten
5656
##################################################
5757

58-
.PHONY: js js-worker
58+
.PHONY: js js-worker js-clean
59+
60+
WORKER_FILENAME := infomap.worker.js
61+
PRE_WORKER_MODULE := interfaces/js/pre-worker-module.js
5962

6063
js: build/js/Infomap.js
6164
@echo "Built $^"
6265

63-
js-worker: build/js/Infomap-worker.js
66+
js-worker: build/js/$(WORKER_FILENAME) Infomap
6467
@echo "Built $^"
68+
@mkdir -p interfaces/js/src/worker
69+
cp build/js/* interfaces/js/src/worker/
70+
npm run build
6571

66-
# em++ -O0 -s PROXY_TO_WORKER=1 -s PROXY_TO_WORKER_FILENAME='Infomap.js' -o Infomap.js $^
67-
# em++ -O0 -s PROXY_TO_WORKER=1 -s EXPORT_NAME='Infomap' -s MODULARIZE=1 -o Infomap.js $^
68-
build/js/Infomap-worker.js: $(SOURCES)
72+
build/js/infomap.worker.js: $(SOURCES) $(PRE_WORKER_MODULE)
6973
@echo "Compiling Infomap to run in a worker in the browser..."
7074
@mkdir -p $(dir $@)
71-
em++ -std=c++14 -O3 -s WASM=0 -s ALLOW_MEMORY_GROWTH=1 --pre-js interfaces/js/pre-worker-module.js -o build/js/Infomap-worker.js $^
75+
em++ -std=c++14 -O3 -s WASM=0 -s ALLOW_MEMORY_GROWTH=1 -s ENVIRONMENT=worker --pre-js $(PRE_WORKER_MODULE) -o build/js/$(WORKER_FILENAME) $(SOURCES)
7276

7377
build/js/Infomap.js: $(SOURCES)
7478
@echo "Compiling Infomap for Node.js..."
7579
@mkdir -p $(dir $@)
7680
em++ -O0 -o build/js/Infomap.js $^
7781

82+
js-clean:
83+
$(RM) -r build/js interfaces/js/src/worker/* dist/*
7884

7985
##################################################
8086
# Static C++ library
Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
#!/usr/bin/env node
21
"use strict";
32

4-
const fsPromises = require("fs").promises;
53
const gitRawCommits = require("git-raw-commits");
64
const conventionalCommitsParser = require("conventional-commits-parser");
75

@@ -13,13 +11,10 @@ function getCommits(from = "", to = "HEAD") {
1311
return new Promise((resolve, reject) =>
1412
gitRawCommits(gitOpts)
1513
.pipe(conventionalCommitsParser())
16-
.on("data", (data) => commits.push(data))
14+
.on("data", data => commits.push(data))
1715
.on("finish", () => resolve(commits))
18-
.on("error", reject));
16+
.on("error", reject)
17+
);
1918
}
2019

21-
function getCommitWriter(filename) {
22-
return commits => fsPromises.writeFile(filename, JSON.stringify(commits, null, 2));
23-
}
24-
25-
module.exports = { getCommits, getCommitWriter };
20+
module.exports = getCommits;

interfaces/js/get-parameters.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const { exec } = require("child_process");
2+
3+
function getParameters(infomapBin) {
4+
return new Promise((resolve, reject) => {
5+
exec(`${infomapBin} --print-json-parameters`, (err, stdout, stderr) => {
6+
if (err) reject(err);
7+
8+
if (stdout) resolve(stdout);
9+
if (stderr) reject(stderr);
10+
});
11+
});
12+
}
13+
14+
module.exports = getParameters;

interfaces/js/pre-worker-module.js

Lines changed: 51 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,61 @@
1-
// var arguments = ["network.txt", "-z", '--clu', "."];
2-
var Module = {};
3-
4-
Module['preRun'] = function infomap_preRun() {
5-
console.log("Pre-run: adding 'filesReady' as run dependency...");
6-
addRunDependency('filesReady');
7-
};
8-
9-
Module['postRun'] = function infomap_postRun() {
10-
console.log("Post-run: Read result...");
11-
var output = {};
12-
var args = Module['arguments'];
13-
// if (args.indexOf('--clu') != -1)
14-
// output.clu = FS.readFile("network.clu", {encoding: "utf8"});
15-
// else
16-
// output.tree = FS.readFile("network.tree", {encoding: "utf8"});
17-
var clu = readFile("network.clu");
18-
if (clu)
19-
output.clu = clu;
20-
var tree = readFile("network.tree");
21-
if (tree)
22-
output.tree = tree;
23-
postMessage({ target: 'finished', output: output});
24-
};
25-
26-
Module['print'] = function Module_print(x) {
27-
//dump('OUT: ' + x + '\n');
28-
postMessage({ target: 'stdout', content: x });
29-
};
30-
Module['printErr'] = function Module_printErr(x) {
31-
//dump('ERR: ' + x + '\n');
32-
postMessage({ target: 'stderr', content: x });
33-
};
34-
351
function readFile(filename) {
36-
var content = '';
2+
var content = "";
373
try {
38-
content = FS.readFile(filename, {encoding: "utf8"});
4+
content = FS.readFile(filename, { encoding: "utf8" });
395
} catch (e) {}
406
return content;
417
}
428

9+
var infomapWorkerId = -1;
10+
11+
var memoryHackRequest = {
12+
status: 200,
13+
useRequest: null,
14+
addEventListener: function(event, callback) {
15+
if (event === "load") {
16+
this.useRequest = callback;
17+
}
18+
}
19+
};
20+
21+
var Module = {
22+
arguments: [],
23+
preRun: function() {
24+
addRunDependency("filesReady");
25+
},
26+
print: function(content) {
27+
postMessage({ type: "data", content, id: infomapWorkerId });
28+
},
29+
printErr: function(content) {
30+
postMessage({ type: "error", content, id: infomapWorkerId });
31+
},
32+
postRun: function() {
33+
var content = {};
34+
var clu = readFile("network.clu");
35+
if (clu) content.clu = clu;
36+
var tree = readFile("network.tree");
37+
if (tree) content.tree = tree;
38+
var ftree = readFile("network.ftree");
39+
if (ftree) content.ftree = ftree;
40+
postMessage({ type: "finished", content, id: infomapWorkerId });
41+
},
42+
memoryInitializerRequest: memoryHackRequest
43+
};
44+
4345
onmessage = function onmessage(message) {
44-
//dump('worker got ' + JSON.stringify(message.data).substr(0, 150) + '\n');
4546
var data = message.data;
46-
switch (data.target) {
47-
case 'Infomap': {
48-
var args = [data.inputFilename, "."];
49-
if (data.arguments)
50-
args = args.concat(data.arguments);
51-
// Module.print("Worker got 'Infomap' with arguments: " + args);
52-
Module['arguments'] = args;
53-
// Module.print("Writing file '" + data.inputFilename + "' with data:\n" + data.inputData);
54-
FS.writeFile(data.inputFilename, data.inputData);
55-
removeRunDependency('filesReady');
56-
break;
57-
}
58-
default:
59-
throw 'Unknown target on message to worker: ' + JSON.stringify(data).substr(0, 150);
47+
48+
if (data.target === "Infomap") {
49+
memoryHackRequest.response = data.memBuffer;
50+
memoryHackRequest.useRequest();
51+
infomapWorkerId = data.id;
52+
var args = [data.inputFilename, "."];
53+
if (data.arguments) args = args.concat(data.arguments);
54+
Module.arguments.push(...args);
55+
FS.writeFile(data.inputFilename, data.inputData);
56+
removeRunDependency("filesReady");
57+
} else {
58+
throw "Unknown target on message to worker: " +
59+
JSON.stringify(data).substr(0, 150);
6060
}
6161
};

interfaces/js/src/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import Infomap from "./infomap.js";
2+
3+
const infomapChangelog = CHANGELOG;
4+
const infomapParameters = PARAMETERS;
5+
6+
export { Infomap as default, infomapChangelog, infomapParameters };

interfaces/js/src/infomap.js

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import InfomapWorker from "./worker/infomap.worker.js";
2+
import MemFile from "./worker/infomap.worker.js.mem";
3+
4+
class Infomap {
5+
static __version__ = VERSION;
6+
7+
_events = {
8+
ondata: () => null,
9+
onerror: () => null,
10+
onfinished: () => null
11+
};
12+
13+
_workerId = 0;
14+
_workers = {};
15+
16+
initWorkerUrl() {
17+
if (this._workerUrl) return;
18+
const blob = new Blob([InfomapWorker], { type: "application/javascript" });
19+
this._workerUrl = URL.createObjectURL(blob);
20+
}
21+
22+
revokeWorkerUrl() {
23+
if (this._workerUrl) URL.revokeObjectURL(this._workerUrl);
24+
}
25+
26+
run(network, args = "") {
27+
if (typeof network !== "string") {
28+
throw new Error("network must be a string");
29+
}
30+
31+
if (typeof args !== "string") {
32+
throw new Error("args must be a string");
33+
}
34+
35+
if (!this._workerUrl) {
36+
this.initWorkerUrl();
37+
}
38+
39+
const worker = new Worker(this._workerUrl);
40+
41+
const id = this._workerId++;
42+
this._workers[id] = worker;
43+
const defaultFilename = "network.net";
44+
45+
worker.postMessage({
46+
memBuffer: new Uint8Array(MemFile),
47+
target: "Infomap",
48+
inputFilename: defaultFilename,
49+
inputData: network,
50+
arguments: args.split(),
51+
id
52+
});
53+
54+
worker.onmessage = this.onmessage;
55+
worker.onerror = err => {
56+
err.preventDefault();
57+
_events.onerror(err.message, id);
58+
};
59+
60+
return id;
61+
}
62+
63+
on(event, callback) {
64+
if (event === "data") this._events.ondata = callback;
65+
else if (event === "error") this._events.onerror = callback;
66+
else if (event === "finished") this._events.onfinished = callback;
67+
else console.warn(`Unhandled event: ${event}`);
68+
69+
return this;
70+
}
71+
72+
onmessage = event => {
73+
const { ondata, onerror, onfinished } = this._events;
74+
const { data } = event;
75+
const { type, content, id } = data;
76+
77+
if (type === "data") {
78+
ondata(content, id);
79+
} else if (type === "error") {
80+
this.cleanup(id);
81+
onerror(content, id);
82+
} else if (type === "finished") {
83+
this.cleanup(id);
84+
onfinished(content, id);
85+
} else {
86+
throw new Error(
87+
`Unknown target on message from worker: ${JSON.stringify(data)}`
88+
);
89+
}
90+
};
91+
92+
cleanup(id) {
93+
if (!this._workers[id]) return;
94+
95+
const worker = this._workers[id];
96+
97+
if (worker.terminate) {
98+
worker.terminate();
99+
}
100+
101+
delete this._workers[id];
102+
}
103+
}
104+
105+
function testInfomap() {
106+
const network =
107+
"#source target [weight]\n 1 2\n 1 3\n 1 4\n 2 1\n 2 3\n 3 2\n 3 1\n 4 1\n 4 5\n 4 6\n 5 4\n 5 6\n 6 5\n 6 4";
108+
109+
const infomap = new Infomap()
110+
.on("data", data => console.log(data))
111+
.on("error", err => console.warn(err))
112+
.on("finished", data => console.log(data));
113+
114+
infomap.run(network);
115+
}
116+
117+
export default Infomap;

interfaces/js/webpack.config.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
const path = require("path");
2+
const DefinePlugin = require("webpack").DefinePlugin;
3+
const getCommits = require("./get-commits.js");
4+
const getParameters = require("./get-parameters.js");
5+
const version = require("../../package.json").version;
6+
7+
const webpackConfig = async () => {
8+
const commits = await getCommits("2d94e92");
9+
const { parameters } = JSON.parse(await getParameters("./Infomap"));
10+
11+
return {
12+
mode: "production",
13+
entry: "./interfaces/js/src/index.js",
14+
devtool: "inline-source-map",
15+
devServer: {
16+
contentBase: "../../dist"
17+
},
18+
output: {
19+
filename: "index.js",
20+
path: path.resolve(__dirname, "../../dist"),
21+
library: "infomap",
22+
libraryTarget: "umd"
23+
},
24+
module: {
25+
rules: [
26+
{
27+
test: /\.worker\.js$/,
28+
use: {
29+
loader: "raw-loader"
30+
}
31+
},
32+
{
33+
test: /\.mem$/,
34+
use: {
35+
loader: "arraybuffer-loader"
36+
}
37+
},
38+
{
39+
test: /\.js$/,
40+
exclude: /(node_modules|utils|worker)/,
41+
use: {
42+
loader: "babel-loader",
43+
options: {
44+
presets: ["@babel/preset-env"],
45+
plugins: ["@babel/plugin-proposal-class-properties"]
46+
}
47+
}
48+
}
49+
]
50+
},
51+
plugins: [
52+
new DefinePlugin({
53+
CHANGELOG: JSON.stringify(commits, null, 2),
54+
VERSION: JSON.stringify(version),
55+
PARAMETERS: JSON.stringify(parameters, null, 2)
56+
})
57+
]
58+
};
59+
};
60+
61+
module.exports = webpackConfig;

0 commit comments

Comments
 (0)