Skip to content
Draft
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
493 changes: 493 additions & 0 deletions lib/lbt/utils/PoolDispatcher.js

Large diffs are not rendered by default.

31 changes: 31 additions & 0 deletions lib/lbt/utils/TaskProcessorThread.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import workerpool from "workerpool";
import {FsWorkerThreadInterface, deserializeResources, serializeData, deserializeData} from "./PoolDispatcher.js";

export default async function execInThread({modulePath, methodName, args}) {
const moduleToExecute = await import(modulePath);
const methodCall = moduleToExecute[methodName] || moduleToExecute["default"];
const {options, resources, fs} = args;

const buildUpArgs = {options: await deserializeData(options)};

if (resources) {
buildUpArgs.resources = await deserializeResources(resources);
}
if (fs) {
buildUpArgs.fs = new FsWorkerThreadInterface(fs);
}

const result = await methodCall(buildUpArgs);

return serializeData(result);
}

// Test execution via ava is never done on the main thread
/* istanbul ignore else */
if (!workerpool.isMainThread) {
// Script got loaded through workerpool
// => Create a worker and register public functions
workerpool.worker({
execInThread,
});
}
47 changes: 12 additions & 35 deletions lib/processors/minifier.js
Original file line number Diff line number Diff line change
@@ -1,45 +1,20 @@
import {fileURLToPath} from "node:url";
import posixPath from "node:path/posix";
import {promisify} from "node:util";
import os from "node:os";
import workerpool from "workerpool";
import {PoolDispatcher} from "../lbt/utils/PoolDispatcher.js";
import Resource from "@ui5/fs/Resource";
import {getLogger} from "@ui5/logger";
const log = getLogger("builder:processors:minifier");

const debugFileRegex = /((?:\.view|\.fragment|\.controller|\.designtime|\.support)?\.js)$/;

const MIN_WORKERS = 2;
const MAX_WORKERS = 4;
const osCpus = os.cpus().length || 1;
const maxWorkers = Math.max(Math.min(osCpus - 1, MAX_WORKERS), MIN_WORKERS);

const sourceMappingUrlPattern = /\/\/# sourceMappingURL=(\S+)\s*$/;
const httpPattern = /^https?:\/\//i;

// Shared workerpool across all executions until the taskUtil cleanup is triggered
let pool;

function getPool(taskUtil) {
if (!pool) {
log.verbose(`Creating workerpool with up to ${maxWorkers} workers (available CPU cores: ${osCpus})`);
const workerPath = fileURLToPath(new URL("./minifierWorker.js", import.meta.url));
pool = workerpool.pool(workerPath, {
workerType: "auto",
maxWorkers
});
taskUtil.registerCleanupTask(() => {
log.verbose(`Terminating workerpool`);
const poolToBeTerminated = pool;
pool = null;
poolToBeTerminated.terminate();
});
}
return pool;
}

async function minifyInWorker(options, taskUtil) {
return getPool(taskUtil).exec("execMinification", [options]);
async function minifyInWorker(args) {
const url = fileURLToPath(new URL("./minifierWorker.js", import.meta.url));
const processor = PoolDispatcher.getInstance().getProcessor(url);
return processor.execute("execMinification", args);
}

async function extractAndRemoveSourceMappingUrl(resource) {
Expand Down Expand Up @@ -249,11 +224,13 @@ export default async function({
}

const result = await minify({
filename,
dbgFilename,
code,
sourceMapOptions
}, taskUtil);
options: {
filename,
dbgFilename,
code,
sourceMapOptions,
},
});
resource.setString(result.code);
const sourceMapResource = new Resource({
path: resource.getPath() + ".map",
Expand Down
66 changes: 27 additions & 39 deletions lib/processors/minifierWorker.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import workerpool from "workerpool";
import {minify} from "terser";

/**
Expand Down Expand Up @@ -27,53 +26,42 @@ const copyrightCommentsAndBundleCommentPattern = /copyright|\(c\)(?:[0-9]+|\s+[0
* @static
*
* @param {object} parameters Parameters
* @param {string} parameters.filename
* @param {string} parameters.dbgFilename
* @param {string} parameters.code
* @param {object} parameters.sourceMapOptions
* @param {string} parameters.options
* @param {string} parameters.options.filename
* @param {string} parameters.options.dbgFilename
* @param {string} parameters.options.code
* @param {object} parameters.options.sourceMapOptions
* @returns {Promise<undefined>} Promise resolving once minification of the resource has finished
*/
export default async function execMinification({
filename,
dbgFilename,
code,
sourceMapOptions
options: {filename, dbgFilename, code, sourceMapOptions},
}) {
try {
return await minify({
// Use debug-name since this will be referenced in the source map "sources"
[dbgFilename]: code
}, {
output: {
comments: copyrightCommentsAndBundleCommentPattern,
wrap_func_args: false
return await minify(
{
// Use debug-name since this will be referenced in the source map "sources"
[dbgFilename]: code,
},
compress: false,
mangle: {
reserved: [
"jQuery",
"jquery",
"sap",
]
},
sourceMap: sourceMapOptions
});
{
output: {
comments: copyrightCommentsAndBundleCommentPattern,
wrap_func_args: false,
},
compress: false,
mangle: {
reserved: ["jQuery", "jquery", "sap"],
},
sourceMap: sourceMapOptions,
}
);
} catch (err) {
// Note: err.filename contains the debug-name
throw new Error(
`Minification failed with error: ${err.message} in file ${filename} ` +
`(line ${err.line}, col ${err.col}, pos ${err.pos})`, {
cause: err
});
`(line ${err.line}, col ${err.col}, pos ${err.pos})`,
{
cause: err,
}
);
}
}

// Test execution via ava is never done on the main thread
/* istanbul ignore else */
if (!workerpool.isMainThread) {
// Script got loaded through workerpool
// => Create a worker and register public functions
workerpool.worker({
execMinification
});
}
Loading