-
Notifications
You must be signed in to change notification settings - Fork 0
/
worker.js
275 lines (251 loc) · 11.1 KB
/
worker.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/**
* Воркер для пула воркеров. Используется для сборки статики и сбора локализуемых фраз.
* @author Kolbeshin F.A.
*/
/* eslint-disable no-console, global-require, no-inner-declarations */
'use strict';
const fs = require('fs-extra');
const { path } = require('../../lib/platform/path');
try {
// increase stack limit to 100 lines to get a better understanding of
// an origin of an error, sometimes default 10 lines of stack of the error
// isn't enough to understand what exactly has happened here
Error.stackTraceLimit = 100;
// логгер - прежде всего
require('../../lib/logger').setWorkerLogger(process.env.logs);
const logger = require('../../lib/logger').logger();
// set information about current cloud to get correct messages (with info about cloud and responsible)
// from worker to be added in a final builder_report
logger.setBaseInfo(process.env.cloud, process.env.responsibleOfCloud);
function initializeWSForWorker() {
// ws должен быть вызван раньше чем первый global.requirejs
const nodeWS = require('./node-ws');
nodeWS.init(JSON.parse(process.env['required-modules']));
}
process.on('unhandledRejection', (reason, p) => {
const error = {
message: `worker's critical error. Unhandled Rejection at:\n ${p}\nreason:\n ${reason}`
};
console.log(error.message);
/**
* write critical initialize error into a single file. workerpool has a problem with emit
* of this errors - all of this errors emits with message "Worker terminated unexpectedly"
* without any message about what's exactly happened inside worker that cause process exit.
*/
fs.outputJsonSync(path.join(process.env.logsPath, `worker/worker-critical-error-${process.pid}.json`), error);
process.exit(1);
});
/**
* require данного набора функционала требует инициализации ядра
* для работы. Поэтому обьявление данных функций выполняем только
* в случае инициализации ядра.
*/
let processingTmpl, prepareXHTMLPrimitive,
buildXhtmlPrimitive, collectWordsPrimitive;
const
workerPool = require('workerpool'),
{ compileEsAndTs } = require('../../lib/compile-es-and-ts'),
{ buildLess } = require('../../lib/less/build-less'),
parseJsComponent = require('../../lib/parse-js-component'),
runMinifyCss = require('../../lib/run-minify-css'),
runMinifyXhtmlAndHtml = require('../../lib/run-minify-xhtml-and-html'),
uglifyJs = require('../../lib/run-uglify-js'),
{ wrapWorkerFunction } = require('./helpers'),
packLibrary = require('../../lib/pack/library-packer'),
{ brotli, gzip } = require('../../lib/helpers'),
loadCompiledJs = require('../../lib/load-compiled-js');
let componentsProperties;
/**
* Прочитать описание компонетов из json для локализации. Или взять прочитанное ранее.
* @param {string} componentsPropertiesFilePath путь до json-файла описания компонентов
* @returns {Promise<Object>}
*/
async function readComponentsProperties(componentsPropertiesFilePath) {
if (!componentsProperties) {
if (await fs.pathExists(componentsPropertiesFilePath)) {
componentsProperties = await fs.readJSON(componentsPropertiesFilePath);
} else {
componentsProperties = {};
}
}
return componentsProperties;
}
/**
* Компиляция tmpl файлов
* @param {string} text содержимое файла
* @param {string} relativeFilePath относительный путь до файла (начинается с имени модуля)
* @param {string} componentsPropertiesFilePath путь до json-файла описания компонентов
* @returns {Promise<{text, nodeName, dependencies}>}
*/
async function buildTmpl(text, relativeFilePath, componentsPropertiesFilePath, generateCodeForTranslations) {
const startTime = Date.now();
if (!processingTmpl) {
initializeWSForWorker();
processingTmpl = require('../../lib/templates/processing-tmpl');
}
const result = await processingTmpl.buildTmpl(
processingTmpl.minifyTmpl(text),
relativeFilePath,
await readComponentsProperties(componentsPropertiesFilePath),
generateCodeForTranslations
);
return Object.assign(result, {
timestamp: {
start: startTime,
finish: Date.now()
}
});
}
/**
* Компиляция html.tmpl файлов
* @param {string} sourceText содержимое файла
* @param {string} fullPath полный путь до файла
* @param {string} relativeFilePath относительный путь до файла (начинается с имени модуля)
* @param {string} componentsPropertiesFilePath путь до json-файла описания компонентов
* @param {boolean} isMultiService является ли проект мультисервисным
* @param {string} servicesPath путь к текущему сервису
* @returns {Promise<string>}
*/
async function buildHtmlTmpl(
sourceText,
fullPath,
serviceConfig,
relativeFilePath,
componentsPropertiesFilePath,
additionalInfo
) {
const startTime = Date.now();
if (!processingTmpl) {
initializeWSForWorker();
processingTmpl = require('../../lib/templates/processing-tmpl');
}
const content = await processingTmpl.buildHtmlTmpl(
sourceText,
fullPath,
serviceConfig,
relativeFilePath,
await readComponentsProperties(componentsPropertiesFilePath),
additionalInfo
);
return {
content,
timestamp: {
start: startTime,
finish: Date.now()
}
};
}
/**
* Для xhtml В XML формате расставляются скобки {[]} - аналог rk - для локализцемых фраз
* (строки в разметке и переводимые опции).
* @param {string} text содержимое файла
* @param {string} componentsPropertiesFilePath путь до json-файла описания компонентов
* @returns {Object}
*/
async function prepareXHTML(text, componentsPropertiesFilePath) {
const startTime = Date.now();
if (!prepareXHTMLPrimitive) {
initializeWSForWorker();
prepareXHTMLPrimitive = require('../../lib/i18n/prepare-xhtml');
}
const newText = await prepareXHTMLPrimitive(text, await readComponentsProperties(componentsPropertiesFilePath));
return {
newText,
timestamp: {
start: startTime,
finish: Date.now()
}
};
}
/**
* Компиляция xhtml в js
* @param {string} text содержимое файла
* @param {string} relativeFilePath относительный путь до файла (начинается с имени модуля)
* @returns {Promise<{nodeName, text}>}
*/
async function buildXhtml(text, relativeFilePath, compilerOptions) {
const startTime = Date.now();
if (!buildXhtmlPrimitive) {
initializeWSForWorker();
buildXhtmlPrimitive = require('../../lib/templates/processing-xhtml').buildXhtml;
}
const content = await buildXhtmlPrimitive(await runMinifyXhtmlAndHtml(text), relativeFilePath, compilerOptions);
return Object.assign(content, {
timestamp: {
start: startTime,
finish: Date.now()
}
});
}
/**
* Сбор локализуемых фрах для конкретного файла
* @param {string} modulePath путь до модуля
* @param {string} filePath путь до файла
* @param {string} componentsPropertiesFilePath путь до json-файла описания компонентов
* @returns {Promise<string[]>}
*/
async function collectWords(modulePath, filePath, componentsPropertiesFilePath) {
if (!componentsProperties) {
componentsProperties = await fs.readJSON(componentsPropertiesFilePath);
}
if (!collectWordsPrimitive) {
initializeWSForWorker();
collectWordsPrimitive = require('../../lib/i18n/collect-words');
}
const text = await fs.readFile(filePath);
return collectWordsPrimitive(modulePath, filePath, text.toString(), componentsProperties);
}
/**
* Get compressed in gzip and brotli data for current text
* @param{String} data - source text
* @returns {Promise<{brotli: *, gzip: *}>}
*/
async function compress(data) {
const startTime = Date.now();
// convert string to buffer. Brotli library can take only buffer as input.
const dataBuffer = Buffer.from(data);
const gzippedContent = await gzip(dataBuffer);
const brotliContent = await brotli(dataBuffer);
return {
gzip: gzippedContent,
brotli: brotliContent,
timestamp: {
start: startTime,
finish: Date.now()
}
};
}
// Read compiled file if we already have a hash for source file in compiled directory
function readCompiledFile(filePath, compiledHashFromCache, hash) {
if (compiledHashFromCache && hash === compiledHashFromCache) {
return fs.readFile(filePath, 'utf8');
}
return '';
}
workerPool.worker({
parseJsComponent: wrapWorkerFunction(parseJsComponent),
buildLess: wrapWorkerFunction(buildLess),
compileEsAndTs: wrapWorkerFunction(compileEsAndTs),
buildTmpl: wrapWorkerFunction(buildTmpl),
readCompiledFile: wrapWorkerFunction(readCompiledFile),
buildHtmlTmpl: wrapWorkerFunction(buildHtmlTmpl),
prepareXHTML: wrapWorkerFunction(prepareXHTML),
buildXhtml: wrapWorkerFunction(buildXhtml),
minifyCss: wrapWorkerFunction(runMinifyCss),
minifyXhtmlAndHtml: wrapWorkerFunction(runMinifyXhtmlAndHtml),
uglifyJs: wrapWorkerFunction(uglifyJs),
compress: wrapWorkerFunction(compress),
collectWords: wrapWorkerFunction(collectWords),
packLibrary: wrapWorkerFunction(packLibrary),
loadCompiledJs: wrapWorkerFunction(loadCompiledJs)
});
} catch (workerInitError) {
const error = { message: `Worker initialize error: ${workerInitError.message} Stack: ${workerInitError.stack}` };
console.log(error.message);
/**
* write critical initialize error into a single file. workerpool has a problem with emit
* of this errors - all of this errors emits with message "Worker terminated unexpectedly"
* without any message about what's exactly happened inside worker that cause process exit.
*/
fs.outputJson(path.join(process.env.logsPath, `worker/worker-initialize-error-${process.pid}.json`), error);
}