Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(index): use local binary from path if found #327

Merged
merged 3 commits into from
Sep 24, 2023
Merged
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
77 changes: 48 additions & 29 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
"use strict";

const { execFile, spawn } = require("node:child_process");
const { promisify } = require("node:util");
const { spawn, spawnSync } = require("node:child_process");
const { readFile } = require("node:fs/promises");
const { gt, lt } = require("semver");
const { joinSafe, normalizeTrim } = require("upath");

const execFileAsync = promisify(execFile);

const errorMessages = {
3221225477: "Segmentation fault",
};
Expand Down Expand Up @@ -77,24 +74,59 @@ function parseOptions(acceptedOptions, options, version) {
}

class UnRTF {
/** @param {string} [binPath] - Path of UnRTF binary. */
/**
* @param {string} [binPath] - Path of UnRTF binary.
* If not provided, the constructor will attempt to find the binary
* in the PATH environment variable.
*
* For `win32`, a binary is bundled with the package and will be used
* if a local installation is not found.
*/
constructor(binPath) {
/* istanbul ignore else: requires specific OS */
if (binPath) {
this.unrtfPath = normalizeTrim(binPath);
} else if (process.platform === "win32") {
this.unrtfPath = joinSafe(
__dirname,
"lib",
"win32",
"unrtf-0.19.3",
"bin"
);
/** @type {string|undefined} */
this.unrtfPath = binPath;
} else {
const { platform } = process;

const which = spawnSync(platform === "win32" ? "where" : "which", [
"unrtf",
]).stdout.toString();
const unrtfPath = /(.+)unrtf/u.exec(which)?.[1];

if (unrtfPath) {
this.unrtfPath = unrtfPath;
}
if (platform === "win32" && !unrtfPath) {
this.unrtfPath = joinSafe(
__dirname,
"lib",
"win32",
"unrtf-0.19.3",
"bin"
);
}
}

if (!this.unrtfPath) {
throw new Error(
`${process.platform} UnRTF binaries are not provided, please pass the installation directory as a parameter to the UnRTF instance.`
`Unable to find ${process.platform} UnRTF binaries, please pass the installation directory as a parameter to the UnRTF instance.`
);
}
this.unrtfPath = normalizeTrim(this.unrtfPath);

/**
* Get version of UnRTF binary for use in `convert` function.
* UnRTF outputs the version into stderr:
* v0.19.3 returns "0.19.3\r\n"
* v0.21.0 returns "0.21.10\nsearch path is: /usr/share/unrtf/\n"
*/
const version = spawnSync(joinSafe(this.unrtfPath, "unrtf"), [
"--version",
]).stderr.toString();
/** @type {string|undefined} */
this.unrtfVersion = /^(\d{1,2}\.\d{1,2}\.\d{1,2})/u.exec(version)?.[1];
}

/**
Expand Down Expand Up @@ -182,20 +214,7 @@ class UnRTF {
);
}

const { stderr } = await execFileAsync(
joinSafe(this.unrtfPath, "unrtf"),
["--version"]
);

/**
* UnRTF outputs the version into stderr:
* v0.19.3 returns "0.19.3\r\n"
* v0.21.0 returns "0.21.10\nsearch path is: /usr/share/unrtf/\n"
*/
// @ts-ignore: parseOptions checks if falsy
const versionInfo = /^(\d{1,2}\.\d{1,2}\.\d{1,2})/u.exec(stderr)[1];

const args = parseOptions(acceptedOptions, options, versionInfo);
const args = parseOptions(acceptedOptions, options, this.unrtfVersion);
args.push(normalizeTrim(file));

return new Promise((resolve, reject) => {
Expand Down
45 changes: 32 additions & 13 deletions src/index.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* eslint-disable jest/no-conditional-expect */
/* eslint-disable global-require, jest/no-conditional-expect */

"use strict";

Expand All @@ -15,7 +15,6 @@
const testDirectory = `${__dirname}/../test_resources/test_files/`;
const file = `${testDirectory}test-rtf-complex.rtf`;

const windowsPath = joinSafe(__dirname, "lib", "win32", "unrtf-0.19.3", "bin");
let testBinaryPath;
switch (process.platform) {
// macOS
Expand All @@ -30,14 +29,20 @@
// Windows OS
case "win32":
default:
testBinaryPath = windowsPath;
testBinaryPath = joinSafe(
__dirname,
"lib",
"win32",
"unrtf-0.19.3",
"bin"
);
break;
}

describe("Constructor", () => {
let platform;

beforeEach(() => {
beforeAll(() => {
// Copy the process platform
({ platform } = process);
});
Expand All @@ -49,27 +54,41 @@
});
});

it("Creates a new UnRTF instance without the binary path set on win32", () => {
Object.defineProperty(process, "platform", {
value: "win32",
});

it("Creates a new UnRTF instance without the binary path set", () => {
const unRtf = new UnRTF();
expect(unRtf.unrtfPath).toBe(windowsPath);
expect(unRtf.unrtfPath).toBe(testBinaryPath);
});

it("Throws an Error if the binary path is not set and the platform is not win32", () => {
/**
* @todo Fix this test, mocking of "node:" scheme not supported yet
* @see {@link https://github.com/jestjs/jest/pull/14297 | Jest PR #14297}
*/
// eslint-disable-next-line jest/no-disabled-tests
it.skip("Throws an Error if the binary path is not found", () => {
Object.defineProperty(process, "platform", {
value: "mockOS",
});

// Ensure the mock is used by the UnRTF constructor
jest.resetModules();
jest.mock("node:child_process", () => ({
spawnSync: jest.fn(() => ({
stdout: {
toString: () => "",
},
})),
}));
// eslint-disable-next-line security/detect-child-process
require("node:child_process");
const { UnRTF: UnRTFMock } = require("./index");

expect.assertions(1);
try {
// eslint-disable-next-line no-unused-vars
const unRtf = new UnRTF();
const unRtf = new UnRTFMock();
Dismissed Show dismissed Hide dismissed
} catch (err) {
expect(err.message).toBe(
`${process.platform} UnRTF binaries are not provided, please pass the installation directory as a parameter to the UnRTF instance.`
`Unable to find ${process.platform} UnRTF binaries, please pass the installation directory as a parameter to the UnRTF instance.`
);
}
});
Expand Down
Loading