1 | var path = require('path');
|
2 | var fork = require('child_process').fork;
|
3 | var assert = require('assert');
|
4 | var HappyLogger = require('./HappyLogger');
|
5 | var HappyRPCHandler = require('./HappyRPCHandler');
|
6 | var Once = require('./fnOnce');
|
7 | var WORKER_BIN = path.resolve(__dirname, 'HappyWorkerChannel.js');
|
8 |
|
9 | function HappyThread(id) {
|
10 | var fd;
|
11 | var callbacks = {};
|
12 |
|
13 | return {
|
14 | open: function(onReady) {
|
15 | var emitReady = Once(onReady);
|
16 |
|
17 | fd = fork(WORKER_BIN, [id]);
|
18 |
|
19 | fd.on('error', throwError);
|
20 | fd.on('exit', function(exitCode) {
|
21 | if (exitCode !== 0) {
|
22 | emitReady('HappyPack: worker exited abnormally with code ' + exitCode);
|
23 | }
|
24 | });
|
25 |
|
26 | fd.on('message', function acceptMessageFromWorker(message) {
|
27 | if (message.name === 'READY') {
|
28 | HappyLogger.info('Happy thread[%d] is now open.', id);
|
29 |
|
30 | emitReady();
|
31 | }
|
32 | else if (message.name === 'COMPILED') {
|
33 | var filePath = message.sourcePath;
|
34 |
|
35 | HappyLogger.info('Thread[%d]: a file has been compiled.', id, filePath);
|
36 |
|
37 | assert(typeof callbacks[filePath] === 'function',
|
38 | "HappyThread: expected loader to be pending on source file '" +
|
39 | filePath + "'" + " (this is likely an internal error!)"
|
40 | );
|
41 |
|
42 | callbacks[filePath](message);
|
43 | delete callbacks[filePath];
|
44 | }
|
45 | else if (message.name === 'COMPILER_REQUEST') {
|
46 | HappyLogger.debug('forwarding compiler request from worker to plugin:', message);
|
47 |
|
48 |
|
49 | HappyRPCHandler.execute(message.data.type, message.data.payload, function(error, result) {
|
50 |
|
51 |
|
52 | fd.send({
|
53 | name: 'COMPILER_RESPONSE',
|
54 | data: {
|
55 | id: message.data.id,
|
56 | payload: {
|
57 | error: error || null,
|
58 | result: result || null
|
59 | }
|
60 | }
|
61 | });
|
62 | });
|
63 | }
|
64 | });
|
65 | },
|
66 |
|
67 | configure: function(compilerOptions, done) {
|
68 | fd.once('message', function(message) {
|
69 | if (message.name === 'CONFIGURE_DONE') {
|
70 | done();
|
71 | }
|
72 | });
|
73 |
|
74 | fd.send({
|
75 | name: 'CONFIGURE',
|
76 | data: {
|
77 | compilerOptions: compilerOptions
|
78 | }
|
79 | });
|
80 | },
|
81 |
|
82 | |
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 | compile: function(params, done) {
|
90 | assert(params.compiledPath && typeof params.compiledPath === 'string');
|
91 | assert(params.loaderContext && typeof params.loaderContext === 'object');
|
92 |
|
93 | assert(!!fd, "You must launch a compilation thread before attemping to use it!!!");
|
94 |
|
95 | callbacks[params.loaderContext.resourcePath] = done;
|
96 |
|
97 | fd.send({
|
98 | name: 'COMPILE',
|
99 | data: params,
|
100 | });
|
101 | },
|
102 |
|
103 | isOpen: function() {
|
104 | return !!fd;
|
105 | },
|
106 |
|
107 | close: function() {
|
108 | fd.kill('SIGINT');
|
109 | fd = null;
|
110 |
|
111 | HappyLogger.info('Happy thread[%d] is now closed.', id);
|
112 | },
|
113 | };
|
114 | }
|
115 |
|
116 | module.exports = HappyThread;
|
117 |
|
118 | function throwError(e) {
|
119 | throw e;
|
120 | }
|