mirror of
https://github.com/GoogleChromeLabs/squoosh.git
synced 2025-11-12 16:57:26 +00:00
CLI code review
This commit is contained in:
BIN
cli/.DS_Store
vendored
Normal file
BIN
cli/.DS_Store
vendored
Normal file
Binary file not shown.
@@ -1,12 +1,12 @@
|
|||||||
import { promises as fsp } from "fs";
|
import { promises as fsp } from 'fs';
|
||||||
|
|
||||||
const prefix = "json:";
|
const prefix = 'json:';
|
||||||
|
|
||||||
const reservedKeys = ["public"];
|
const reservedKeys = ['public'];
|
||||||
|
|
||||||
export default function jsonPlugin() {
|
export default function jsonPlugin() {
|
||||||
return {
|
return {
|
||||||
name: "json-plugin",
|
name: 'json-plugin',
|
||||||
async resolveId(id, importer) {
|
async resolveId(id, importer) {
|
||||||
if (!id.startsWith(prefix)) return;
|
if (!id.startsWith(prefix)) return;
|
||||||
const realId = id.slice(prefix.length);
|
const realId = id.slice(prefix.length);
|
||||||
@@ -21,18 +21,18 @@ export default function jsonPlugin() {
|
|||||||
async load(id) {
|
async load(id) {
|
||||||
if (!id.startsWith(prefix)) return;
|
if (!id.startsWith(prefix)) return;
|
||||||
const realId = id.slice(prefix.length);
|
const realId = id.slice(prefix.length);
|
||||||
const source = await fsp.readFile(realId, "utf8");
|
const source = await fsp.readFile(realId, 'utf8');
|
||||||
|
|
||||||
let code = "";
|
let code = '';
|
||||||
for (const [key, value] of Object.entries(JSON.parse(source))) {
|
for (const [key, value] of Object.entries(JSON.parse(source))) {
|
||||||
if (reservedKeys.includes(key)) {
|
if (reservedKeys.includes(key)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
code += `
|
code += `
|
||||||
export const ${key} = ${JSON.stringify(value)};
|
export const ${key} = ${JSON.stringify(value)};
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
return code;
|
return code;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
cli/out/02-01-implementations-1.jxl
Normal file
BIN
cli/out/02-01-implementations-1.jxl
Normal file
Binary file not shown.
BIN
cli/out/02-01-implementations-1.webp
Normal file
BIN
cli/out/02-01-implementations-1.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
BIN
cli/out/02-01-implementations-1.wp2
Normal file
BIN
cli/out/02-01-implementations-1.wp2
Normal file
Binary file not shown.
BIN
cli/out/830A0062.jpg
Normal file
BIN
cli/out/830A0062.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 65 KiB |
@@ -1,13 +1,16 @@
|
|||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
export function pathify(path) {
|
export function pathify(path) {
|
||||||
if (path.startsWith("file://")) {
|
if (path.startsWith('file://')) {
|
||||||
path = path.slice("file://".length);
|
path = fileURLToPath(path);
|
||||||
}
|
}
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function instantiateEmscriptenWasm(factory, path) {
|
export function instantiateEmscriptenWasm(factory, path) {
|
||||||
return factory({
|
return factory({
|
||||||
locateFile() {
|
locateFile() {
|
||||||
return pathify(path);
|
return pathify(path);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
155
cli/src/index.js
155
cli/src/index.js
@@ -1,16 +1,16 @@
|
|||||||
import { program } from "commander";
|
import { program } from 'commander';
|
||||||
import JSON5 from "json5";
|
import JSON5 from 'json5';
|
||||||
import { isMainThread } from "worker_threads";
|
import { isMainThread } from 'worker_threads';
|
||||||
import { cpus } from "os";
|
import { cpus } from 'os';
|
||||||
import { extname, join, basename } from "path";
|
import { extname, join, basename } from 'path';
|
||||||
import { promises as fsp } from "fs";
|
import { promises as fsp } from 'fs';
|
||||||
import { version } from "json:../package.json";
|
import { version } from 'json:../package.json';
|
||||||
import ora from "ora";
|
import ora from 'ora';
|
||||||
import kleur from "kleur";
|
import kleur from 'kleur';
|
||||||
|
|
||||||
import { codecs as supportedFormats, preprocessors } from "./codecs.js";
|
import { codecs as supportedFormats, preprocessors } from './codecs.js';
|
||||||
import WorkerPool from "./worker_pool.js";
|
import WorkerPool from './worker_pool.js';
|
||||||
import { autoOptimize } from "./auto-optimizer.js";
|
import { autoOptimize } from './auto-optimizer.js';
|
||||||
|
|
||||||
function clamp(v, min, max) {
|
function clamp(v, min, max) {
|
||||||
if (v < min) return min;
|
if (v < min) return min;
|
||||||
@@ -18,7 +18,7 @@ function clamp(v, min, max) {
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
const suffix = ["B", "KB", "MB"];
|
const suffix = ['B', 'KB', 'MB'];
|
||||||
function prettyPrintSize(size) {
|
function prettyPrintSize(size) {
|
||||||
const base = Math.floor(Math.log2(size) / 10);
|
const base = Math.floor(Math.log2(size) / 10);
|
||||||
const index = clamp(base, 0, 2);
|
const index = clamp(base, 0, 2);
|
||||||
@@ -29,21 +29,21 @@ async function decodeFile(file) {
|
|||||||
const buffer = await fsp.readFile(file);
|
const buffer = await fsp.readFile(file);
|
||||||
const firstChunk = buffer.slice(0, 16);
|
const firstChunk = buffer.slice(0, 16);
|
||||||
const firstChunkString = Array.from(firstChunk)
|
const firstChunkString = Array.from(firstChunk)
|
||||||
.map(v => String.fromCodePoint(v))
|
.map((v) => String.fromCodePoint(v))
|
||||||
.join("");
|
.join('');
|
||||||
const key = Object.entries(supportedFormats).find(([name, { detectors }]) =>
|
const key = Object.entries(supportedFormats).find(([name, { detectors }]) =>
|
||||||
detectors.some(detector => detector.exec(firstChunkString))
|
detectors.some((detector) => detector.exec(firstChunkString)),
|
||||||
)?.[0];
|
)?.[0];
|
||||||
if (!key) {
|
if (!key) {
|
||||||
throw Error(`${file} has an unsupported format`);
|
throw Error(`${file} has an unsupported format`);
|
||||||
}
|
}
|
||||||
const rgba = (await supportedFormats[key].dec()).decode(
|
const rgba = (await supportedFormats[key].dec()).decode(
|
||||||
new Uint8Array(buffer)
|
new Uint8Array(buffer),
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
file,
|
file,
|
||||||
bitmap: rgba,
|
bitmap: rgba,
|
||||||
size: buffer.length
|
size: buffer.length,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ async function preprocessImage({ preprocessorName, options, file }) {
|
|||||||
file.bitmap.data,
|
file.bitmap.data,
|
||||||
file.bitmap.width,
|
file.bitmap.width,
|
||||||
file.bitmap.height,
|
file.bitmap.height,
|
||||||
options
|
options,
|
||||||
);
|
);
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
@@ -66,11 +66,11 @@ async function encodeFile({
|
|||||||
encName,
|
encName,
|
||||||
encConfig,
|
encConfig,
|
||||||
optimizerButteraugliTarget,
|
optimizerButteraugliTarget,
|
||||||
maxOptimizerRounds
|
maxOptimizerRounds,
|
||||||
}) {
|
}) {
|
||||||
let out, infoText;
|
let out, infoText;
|
||||||
const encoder = await supportedFormats[encName].enc();
|
const encoder = await supportedFormats[encName].enc();
|
||||||
if (encConfig === "auto") {
|
if (encConfig === 'auto') {
|
||||||
const optionToOptimize = supportedFormats[encName].autoOptimize.option;
|
const optionToOptimize = supportedFormats[encName].autoOptimize.option;
|
||||||
const decoder = await supportedFormats[encName].dec();
|
const decoder = await supportedFormats[encName].dec();
|
||||||
const encode = (bitmapIn, quality) =>
|
const encode = (bitmapIn, quality) =>
|
||||||
@@ -79,10 +79,10 @@ async function encodeFile({
|
|||||||
bitmapIn.width,
|
bitmapIn.width,
|
||||||
bitmapIn.height,
|
bitmapIn.height,
|
||||||
Object.assign({}, supportedFormats[encName].defaultEncoderOptions, {
|
Object.assign({}, supportedFormats[encName].defaultEncoderOptions, {
|
||||||
[optionToOptimize]: quality
|
[optionToOptimize]: quality,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
const decode = binary => decoder.decode(binary);
|
const decode = (binary) => decoder.decode(binary);
|
||||||
const { bitmap, binary, quality } = await autoOptimize(
|
const { bitmap, binary, quality } = await autoOptimize(
|
||||||
bitmapIn,
|
bitmapIn,
|
||||||
encode,
|
encode,
|
||||||
@@ -91,13 +91,13 @@ async function encodeFile({
|
|||||||
min: supportedFormats[encName].autoOptimize.min,
|
min: supportedFormats[encName].autoOptimize.min,
|
||||||
max: supportedFormats[encName].autoOptimize.max,
|
max: supportedFormats[encName].autoOptimize.max,
|
||||||
butteraugliDistanceGoal: optimizerButteraugliTarget,
|
butteraugliDistanceGoal: optimizerButteraugliTarget,
|
||||||
maxRounds: maxOptimizerRounds
|
maxRounds: maxOptimizerRounds,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
out = binary;
|
out = binary;
|
||||||
const opts = {
|
const opts = {
|
||||||
// 5 significant digits is enough
|
// 5 significant digits is enough
|
||||||
[optionToOptimize]: Math.round(quality * 10000) / 10000
|
[optionToOptimize]: Math.round(quality * 10000) / 10000,
|
||||||
};
|
};
|
||||||
infoText = ` using --${encName} '${JSON5.stringify(opts)}'`;
|
infoText = ` using --${encName} '${JSON5.stringify(opts)}'`;
|
||||||
} else {
|
} else {
|
||||||
@@ -105,7 +105,7 @@ async function encodeFile({
|
|||||||
bitmapIn.data.buffer,
|
bitmapIn.data.buffer,
|
||||||
bitmapIn.width,
|
bitmapIn.width,
|
||||||
bitmapIn.height,
|
bitmapIn.height,
|
||||||
encConfig
|
encConfig,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
await fsp.writeFile(outputFile, out);
|
await fsp.writeFile(outputFile, out);
|
||||||
@@ -114,7 +114,7 @@ async function encodeFile({
|
|||||||
inputSize: size,
|
inputSize: size,
|
||||||
inputFile: file,
|
inputFile: file,
|
||||||
outputFile,
|
outputFile,
|
||||||
outputSize: out.length
|
outputSize: out.length,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,11 +122,11 @@ async function encodeFile({
|
|||||||
function handleJob(params) {
|
function handleJob(params) {
|
||||||
const { operation } = params;
|
const { operation } = params;
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
case "encode":
|
case 'encode':
|
||||||
return encodeFile(params);
|
return encodeFile(params);
|
||||||
case "decode":
|
case 'decode':
|
||||||
return decodeFile(params.file);
|
return decodeFile(params.file);
|
||||||
case "preprocess":
|
case 'preprocess':
|
||||||
return preprocessImage(params);
|
return preprocessImage(params);
|
||||||
default:
|
default:
|
||||||
throw Error(`Invalid job "${operation}"`);
|
throw Error(`Invalid job "${operation}"`);
|
||||||
@@ -139,44 +139,44 @@ function progressTracker(results) {
|
|||||||
tracker.spinner = spinner;
|
tracker.spinner = spinner;
|
||||||
tracker.progressOffset = 0;
|
tracker.progressOffset = 0;
|
||||||
tracker.totalOffset = 0;
|
tracker.totalOffset = 0;
|
||||||
let status = "";
|
let status = '';
|
||||||
tracker.setStatus = text => {
|
tracker.setStatus = (text) => {
|
||||||
status = text || "";
|
status = text || '';
|
||||||
update();
|
update();
|
||||||
};
|
};
|
||||||
let progress = "";
|
let progress = '';
|
||||||
tracker.setProgress = (done, total) => {
|
tracker.setProgress = (done, total) => {
|
||||||
spinner.prefixText = kleur.dim(`${done}/${total}`);
|
spinner.prefixText = kleur.dim(`${done}/${total}`);
|
||||||
const completeness =
|
const completeness =
|
||||||
(tracker.progressOffset + done) / (tracker.totalOffset + total);
|
(tracker.progressOffset + done) / (tracker.totalOffset + total);
|
||||||
progress = kleur.cyan(
|
progress = kleur.cyan(
|
||||||
`▐${"▨".repeat((completeness * 10) | 0).padEnd(10, "╌")}▌ `
|
`▐${'▨'.repeat((completeness * 10) | 0).padEnd(10, '╌')}▌ `,
|
||||||
);
|
);
|
||||||
update();
|
update();
|
||||||
};
|
};
|
||||||
function update() {
|
function update() {
|
||||||
spinner.text = progress + kleur.bold(status) + getResultsText();
|
spinner.text = progress + kleur.bold(status) + getResultsText();
|
||||||
}
|
}
|
||||||
tracker.finish = text => {
|
tracker.finish = (text) => {
|
||||||
spinner.succeed(kleur.bold(text) + getResultsText());
|
spinner.succeed(kleur.bold(text) + getResultsText());
|
||||||
};
|
};
|
||||||
function getResultsText() {
|
function getResultsText() {
|
||||||
let out = "";
|
let out = '';
|
||||||
for (const [filename, result] of results.entries()) {
|
for (const [filename, result] of results.entries()) {
|
||||||
out += `\n ${kleur.cyan(filename)}: ${prettyPrintSize(result.size)}`;
|
out += `\n ${kleur.cyan(filename)}: ${prettyPrintSize(result.size)}`;
|
||||||
for (const { outputFile, outputSize, infoText } of result.outputs) {
|
for (const { outputFile, outputSize, infoText } of result.outputs) {
|
||||||
const name = (program.suffix + extname(outputFile)).padEnd(5);
|
const name = (program.suffix + extname(outputFile)).padEnd(5);
|
||||||
out += `\n ${kleur.dim("└")} ${kleur.cyan(name)} → ${prettyPrintSize(
|
out += `\n ${kleur.dim('└')} ${kleur.cyan(name)} → ${prettyPrintSize(
|
||||||
outputSize
|
outputSize,
|
||||||
)}`;
|
)}`;
|
||||||
const percent = ((outputSize / result.size) * 100).toPrecision(3);
|
const percent = ((outputSize / result.size) * 100).toPrecision(3);
|
||||||
out += ` (${kleur[outputSize > result.size ? "red" : "green"](
|
out += ` (${kleur[outputSize > result.size ? 'red' : 'green'](
|
||||||
percent + "%"
|
percent + '%',
|
||||||
)})`;
|
)})`;
|
||||||
if (infoText) out += kleur.yellow(infoText);
|
if (infoText) out += kleur.yellow(infoText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out || "\n";
|
return out || '\n';
|
||||||
}
|
}
|
||||||
spinner.start();
|
spinner.start();
|
||||||
return tracker;
|
return tracker;
|
||||||
@@ -188,7 +188,7 @@ async function processFiles(files) {
|
|||||||
const results = new Map();
|
const results = new Map();
|
||||||
const progress = progressTracker(results);
|
const progress = progressTracker(results);
|
||||||
|
|
||||||
progress.setStatus("Decoding...");
|
progress.setStatus('Decoding...');
|
||||||
progress.totalOffset = files.length;
|
progress.totalOffset = files.length;
|
||||||
progress.setProgress(0, files.length);
|
progress.setProgress(0, files.length);
|
||||||
|
|
||||||
@@ -198,19 +198,19 @@ async function processFiles(files) {
|
|||||||
|
|
||||||
let decoded = 0;
|
let decoded = 0;
|
||||||
let decodedFiles = await Promise.all(
|
let decodedFiles = await Promise.all(
|
||||||
files.map(async file => {
|
files.map(async (file) => {
|
||||||
const result = await workerPool.dispatchJob({
|
const result = await workerPool.dispatchJob({
|
||||||
operation: "decode",
|
operation: 'decode',
|
||||||
file
|
file,
|
||||||
});
|
});
|
||||||
results.set(file, {
|
results.set(file, {
|
||||||
file: result.file,
|
file: result.file,
|
||||||
size: result.size,
|
size: result.size,
|
||||||
outputs: []
|
outputs: [],
|
||||||
});
|
});
|
||||||
progress.setProgress(++decoded, files.length);
|
progress.setProgress(++decoded, files.length);
|
||||||
return result;
|
return result;
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const [preprocessorName, value] of Object.entries(preprocessors)) {
|
for (const [preprocessorName, value] of Object.entries(preprocessors)) {
|
||||||
@@ -221,26 +221,23 @@ async function processFiles(files) {
|
|||||||
const preprocessorOptions = Object.assign(
|
const preprocessorOptions = Object.assign(
|
||||||
{},
|
{},
|
||||||
value.defaultOptions,
|
value.defaultOptions,
|
||||||
JSON5.parse(preprocessorParam)
|
JSON5.parse(preprocessorParam),
|
||||||
);
|
);
|
||||||
|
|
||||||
decodedFiles = await Promise.all(
|
decodedFiles = await Promise.all(
|
||||||
decodedFiles.map(async file => {
|
decodedFiles.map(async (file) => {
|
||||||
return workerPool.dispatchJob({
|
return workerPool.dispatchJob({
|
||||||
file,
|
file,
|
||||||
operation: "preprocess",
|
operation: 'preprocess',
|
||||||
preprocessorName,
|
preprocessorName,
|
||||||
options: preprocessorOptions
|
options: preprocessorOptions,
|
||||||
});
|
});
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const { file, bitmap, size } of decodedFiles) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
progress.progressOffset = decoded;
|
progress.progressOffset = decoded;
|
||||||
progress.setStatus("Encoding " + kleur.dim(`(${parallelism} threads)`));
|
progress.setStatus('Encoding ' + kleur.dim(`(${parallelism} threads)`));
|
||||||
progress.setProgress(0, files.length);
|
progress.setProgress(0, files.length);
|
||||||
|
|
||||||
const jobs = [];
|
const jobs = [];
|
||||||
@@ -255,20 +252,20 @@ async function processFiles(files) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const encParam =
|
const encParam =
|
||||||
typeof program[encName] === "string" ? program[encName] : "{}";
|
typeof program[encName] === 'string' ? program[encName] : '{}';
|
||||||
const encConfig =
|
const encConfig =
|
||||||
encParam.toLowerCase() === "auto"
|
encParam.toLowerCase() === 'auto'
|
||||||
? "auto"
|
? 'auto'
|
||||||
: Object.assign(
|
: Object.assign(
|
||||||
{},
|
{},
|
||||||
value.defaultEncoderOptions,
|
value.defaultEncoderOptions,
|
||||||
JSON5.parse(encParam)
|
JSON5.parse(encParam),
|
||||||
);
|
);
|
||||||
const outputFile = join(program.outputDir, `${base}.${value.extension}`);
|
const outputFile = join(program.outputDir, `${base}.${value.extension}`);
|
||||||
jobsStarted++;
|
jobsStarted++;
|
||||||
const p = workerPool
|
const p = workerPool
|
||||||
.dispatchJob({
|
.dispatchJob({
|
||||||
operation: "encode",
|
operation: 'encode',
|
||||||
file,
|
file,
|
||||||
size,
|
size,
|
||||||
bitmap,
|
bitmap,
|
||||||
@@ -276,11 +273,11 @@ async function processFiles(files) {
|
|||||||
encName,
|
encName,
|
||||||
encConfig,
|
encConfig,
|
||||||
optimizerButteraugliTarget: Number(
|
optimizerButteraugliTarget: Number(
|
||||||
program.optimizerButteraugliTarget
|
program.optimizerButteraugliTarget,
|
||||||
),
|
),
|
||||||
maxOptimizerRounds: Number(program.maxOptimizerRounds)
|
maxOptimizerRounds: Number(program.maxOptimizerRounds),
|
||||||
})
|
})
|
||||||
.then(output => {
|
.then((output) => {
|
||||||
jobsFinished++;
|
jobsFinished++;
|
||||||
results.get(file).outputs.push(output);
|
results.get(file).outputs.push(output);
|
||||||
progress.setProgress(jobsFinished, jobsStarted);
|
progress.setProgress(jobsFinished, jobsStarted);
|
||||||
@@ -294,25 +291,25 @@ async function processFiles(files) {
|
|||||||
// Wait for all jobs to finish
|
// Wait for all jobs to finish
|
||||||
await workerPool.join();
|
await workerPool.join();
|
||||||
await Promise.all(jobs);
|
await Promise.all(jobs);
|
||||||
progress.finish("Squoosh results:");
|
progress.finish('Squoosh results:');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMainThread) {
|
if (isMainThread) {
|
||||||
program
|
program
|
||||||
.name("squoosh-cli")
|
.name('squoosh-cli')
|
||||||
.version(version)
|
.version(version)
|
||||||
.arguments("<files...>")
|
.arguments('<files...>')
|
||||||
.option("-d, --output-dir <dir>", "Output directory", ".")
|
.option('-d, --output-dir <dir>', 'Output directory', '.')
|
||||||
.option("-s, --suffix <suffix>", "Append suffix to output files", "")
|
.option('-s, --suffix <suffix>', 'Append suffix to output files', '')
|
||||||
.option(
|
.option(
|
||||||
"--max-optimizer-rounds <rounds>",
|
'--max-optimizer-rounds <rounds>',
|
||||||
"Maximum number of compressions to use for auto optimizations",
|
'Maximum number of compressions to use for auto optimizations',
|
||||||
"6"
|
'6',
|
||||||
)
|
)
|
||||||
.option(
|
.option(
|
||||||
"--optimizer-butteraugli-target <butteraugli distance>",
|
'--optimizer-butteraugli-target <butteraugli distance>',
|
||||||
"Target Butteraugli distance for auto optimizer",
|
'Target Butteraugli distance for auto optimizer',
|
||||||
"1.4"
|
'1.4',
|
||||||
)
|
)
|
||||||
.action(processFiles);
|
.action(processFiles);
|
||||||
|
|
||||||
@@ -324,7 +321,7 @@ if (isMainThread) {
|
|||||||
for (const [key, value] of Object.entries(supportedFormats)) {
|
for (const [key, value] of Object.entries(supportedFormats)) {
|
||||||
program.option(
|
program.option(
|
||||||
`--${key} [config]`,
|
`--${key} [config]`,
|
||||||
`Use ${value.name} to generate a .${value.extension} file with the given configuration`
|
`Use ${value.name} to generate a .${value.extension} file with the given configuration`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +1,24 @@
|
|||||||
import { Worker, parentPort } from "worker_threads";
|
import { Worker, parentPort } from 'worker_threads';
|
||||||
import { TransformStream } from "web-streams-polyfill";
|
import { TransformStream } from 'web-streams-polyfill';
|
||||||
|
|
||||||
function uuid() {
|
function uuid() {
|
||||||
return Array.from({ length: 16 }, () =>
|
return Array.from({ length: 16 }, () =>
|
||||||
Math.floor(Math.random() * 256).toString(16)
|
Math.floor(Math.random() * 256).toString(16),
|
||||||
).join("");
|
).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
function jobPromise(worker, msg) {
|
function jobPromise(worker, msg) {
|
||||||
return new Promise(resolve => {
|
return new Promise((resolve) => {
|
||||||
const id = uuid();
|
const id = uuid();
|
||||||
worker.postMessage({ msg, id });
|
worker.postMessage({ msg, id });
|
||||||
worker.on("message", function f({ result, id: rid }) {
|
worker.on('message', function f({ result, id: rid }) {
|
||||||
if (rid !== id) {
|
if (rid !== id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
worker.off("message", f);
|
worker.off('message', f);
|
||||||
resolve(result);
|
resolve(result);
|
||||||
});
|
});
|
||||||
|
worker.on('error', (error) => console.error('Worker error: ', error));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +48,7 @@ export default class WorkerPool {
|
|||||||
}
|
}
|
||||||
const { msg, resolve } = value;
|
const { msg, resolve } = value;
|
||||||
const worker = await this._nextWorker();
|
const worker = await this._nextWorker();
|
||||||
jobPromise(worker, msg).then(result => {
|
jobPromise(worker, msg).then((result) => {
|
||||||
resolve(result);
|
resolve(result);
|
||||||
// If we are in the process of closing, `workerQueue` is
|
// If we are in the process of closing, `workerQueue` is
|
||||||
// already closed and we can’t requeue the worker.
|
// already closed and we can’t requeue the worker.
|
||||||
@@ -86,7 +87,7 @@ export default class WorkerPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dispatchJob(msg) {
|
dispatchJob(msg) {
|
||||||
return new Promise(resolve => {
|
return new Promise((resolve) => {
|
||||||
const writer = this.jobQueue.writable.getWriter();
|
const writer = this.jobQueue.writable.getWriter();
|
||||||
writer.write({ msg, resolve });
|
writer.write({ msg, resolve });
|
||||||
writer.releaseLock();
|
writer.releaseLock();
|
||||||
@@ -94,7 +95,7 @@ export default class WorkerPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static useThisThreadAsWorker(cb) {
|
static useThisThreadAsWorker(cb) {
|
||||||
parentPort.on("message", async data => {
|
parentPort.on('message', async (data) => {
|
||||||
const { msg, id } = data;
|
const { msg, id } = data;
|
||||||
const result = await cb(msg);
|
const result = await cb(msg);
|
||||||
parentPort.postMessage({ result, id });
|
parentPort.postMessage({ result, id });
|
||||||
|
|||||||
12
cli/tmp.txt
Normal file
12
cli/tmp.txt
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user