From a7319f69689ada7f712bb42874f0ea15d7812bb4 Mon Sep 17 00:00:00 2001 From: Frazer Smith Date: Tue, 12 Mar 2024 17:18:12 +0000 Subject: [PATCH] feat(index): use binary from path env variable if not passed as param --- src/index.js | 48 ++++++++++++++++++++++++++++++++++------------- src/index.test.js | 26 +++++++++++++++++++++---- 2 files changed, 57 insertions(+), 17 deletions(-) diff --git a/src/index.js b/src/index.js index 385def7e..0b4db5b8 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,6 @@ "use strict"; -const { execFile, spawn } = require("node:child_process"); +const { execFile, spawn, spawnSync } = require("node:child_process"); const { promisify } = require("node:util"); const camelCase = require("camelcase"); const { lt } = require("semver"); @@ -84,24 +84,46 @@ function parseOptions(acceptedOptions, options, version) { } class Poppler { - /** @param {string} [binPath] - Path of poppler-utils binaries. */ + /** + * @param {string} [binPath] - Path of poppler-utils binaries. + * If not provided, the constructor will attempt to find the Poppler `pdfinfo` binary + * in the PATH environment variable and use that as the binary path for all binaries. + * For `win32` the binary is bundled with the package and will be used + * if a local installation is not found. + */ constructor(binPath) { if (binPath) { - this.popplerPath = normalizeTrim(binPath); - } else if (process.platform === "win32") { - this.popplerPath = joinSafe( - __dirname, - "lib", - "win32", - "poppler-24.02.0", - "Library", - "bin" - ); + this.popplerPath = binPath; } else { + const { platform } = process; + + const which = spawnSync(platform === "win32" ? "where" : "which", [ + "pdfinfo", + ]).stdout.toString(); + const popplerPath = /(.+)pdfinfo/u.exec(which)?.[1]; + + if (popplerPath) { + this.popplerPath = popplerPath; + } + if (platform === "win32" && !popplerPath) { + this.popplerPath = joinSafe( + __dirname, + "lib", + "win32", + "poppler-24.02.0", + "Library", + "bin" + ); + } + } + + /* istanbul ignore next: unable to test due to https://github.com/jestjs/jest/pull/14297 */ + if (!this.popplerPath) { throw new Error( - `${process.platform} poppler-util binaries are not provided, please pass the installation directory as a parameter to the Poppler instance.` + `Unable to find ${process.platform} Poppler binaries, please pass the installation directory as a parameter to the Poppler instance.` ); } + this.popplerPath = normalizeTrim(this.popplerPath); } /** diff --git a/src/index.test.js b/src/index.test.js index aa18d741..68b24335 100644 --- a/src/index.test.js +++ b/src/index.test.js @@ -1,3 +1,4 @@ +/* eslint-disable global-require, security/detect-child-process -- Mocking child_process */ /* eslint-disable jest/no-conditional-expect -- Depends on the version of the binary */ /* eslint-disable security/detect-non-literal-fs-filename -- Test files are not user-provided */ @@ -60,7 +61,7 @@ describe("Node-Poppler module", () => { describe("Constructor", () => { let platform; - beforeEach(() => { + beforeAll(() => { // Copy the process platform ({ platform } = process); }); @@ -81,18 +82,35 @@ describe("Node-Poppler module", () => { expect(poppler.popplerPath).toBe(windowsPath); }); - 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 -- Blocked by Jest PR #14297 + 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: () => "", + }, + })), + })); + require("node:child_process"); + const { Poppler: PopplerMock } = require("./index"); + expect.assertions(1); try { // eslint-disable-next-line no-unused-vars -- This is intentional - const poppler = new Poppler(); + const poppler = new PopplerMock(); } catch (err) { expect(err.message).toBe( - `${process.platform} poppler-util binaries are not provided, please pass the installation directory as a parameter to the Poppler instance.` + `Unable to find ${process.platform} Poppler binaries, please pass the installation directory as a parameter to the Poppler instance.` ); } });