From c11e616a5a98fd69ffba2e3ae7c7567f977c8cdf Mon Sep 17 00:00:00 2001 From: Yuki Hattori Date: Fri, 17 Jan 2025 23:55:45 +0900 Subject: [PATCH] Improve SOffice binary finder and handler - Removed Windows LibreOffice app transparency from WSL. - WSL-specific finder is no longer used. - Detect LibreOffice installed via Scoop in Windows. (#631) - Update error level of soffice stderr from ERROR to WARN. --- src/soffice/finder.ts | 51 ++++++++-------------------------------- src/soffice/soffice.ts | 53 ++++++++++++------------------------------ 2 files changed, 25 insertions(+), 79 deletions(-) diff --git a/src/soffice/finder.ts b/src/soffice/finder.ts index 964eeefd..5488b04d 100644 --- a/src/soffice/finder.ts +++ b/src/soffice/finder.ts @@ -7,7 +7,8 @@ import { isExecutable, normalizeDarwinAppPath, } from '../utils/finder' -import { getWSL2NetworkingMode } from '../utils/wsl' + +const scoopRestPath = ['scoop', 'apps', 'libreoffice', 'current'] as const const sOffice = (path: string) => ({ path }) as const @@ -28,11 +29,9 @@ export const findSOffice = async ({ return await sOfficeFinderDarwin() case 'win32': return await sOfficeFinderWin32() - case 'wsl1': - return await sOfficeFinderWSL() } - return await sOfficeFinderLinuxOrFallback() + return await sOfficeFinderLinux() })() if (installation) return sOffice(installation) @@ -73,49 +72,19 @@ const sOfficeFinderWin32 = async () => { } } - return await findExecutable( - prefixes.map((prefix) => - path.join(prefix, 'LibreOffice', 'program', 'soffice.exe') - ) - ) -} - -const sOfficeFinderWSL = async () => { - const prefixes: string[] = [] - - const winDriveMatcher = /^\/mnt\/[a-z]\//i - const winPossibleDrives = () => { - const possibleDriveSet = new Set(['c']) - const pathEnvs = process.env.PATH?.split(':') ?? [] - - for (const pathEnv of pathEnvs) { - if (winDriveMatcher.test(pathEnv)) { - possibleDriveSet.add(pathEnv[5].toLowerCase()) - } - } - - return Array.from(possibleDriveSet).sort() + // Scoop + if (process.env.USERPROFILE) { + prefixes.push(path.join(process.env.USERPROFILE, ...scoopRestPath)) } - - for (const drive of winPossibleDrives()) { - prefixes.push(`/mnt/${drive}/Program Files`) - prefixes.push(`/mnt/${drive}/Program Files (x86)`) + if (process.env.ALLUSERSPROFILE) { + prefixes.push(path.join(process.env.ALLUSERSPROFILE, ...scoopRestPath)) } return await findExecutable( prefixes.map((prefix) => - path.posix.join(prefix, 'LibreOffice', 'program', 'soffice.exe') + path.join(prefix, 'LibreOffice', 'program', 'soffice.exe') ) ) } -const sOfficeFinderLinuxOrFallback = async () => { - const ret = await findExecutableBinary(['soffice']) - if (ret) return ret - - // WSL2 Fallback - if ((await getWSL2NetworkingMode()) === 'mirrored') - return await sOfficeFinderWSL() - - return undefined -} +const sOfficeFinderLinux = async () => await findExecutableBinary(['soffice']) diff --git a/src/soffice/soffice.ts b/src/soffice/soffice.ts index f33992d0..1d92e09f 100644 --- a/src/soffice/soffice.ts +++ b/src/soffice/soffice.ts @@ -5,26 +5,26 @@ import path from 'node:path' import { pathToFileURL } from 'node:url' import chalk from 'chalk' import { nanoid } from 'nanoid' -import { error } from '../cli' +import { warn } from '../cli' import { debug } from '../utils/debug' import { createMemoizedPromiseContext } from '../utils/memoized-promise' -import { getWindowsEnv, isWSL, translateWindowsPathToWSL } from '../utils/wsl' import { findSOffice } from './finder' -const wslHostMatcher = /^\/mnt\/[a-z]\// - -let wslTmp: string | undefined - export interface SOfficeOptions { path?: string } +export interface SOfficeProfileDir { + path: string + fileURL: string +} + export class SOffice { preferredPath?: string #profileDirName: string private _path = createMemoizedPromiseContext() - private _profileDir = createMemoizedPromiseContext() + private _profileDir = createMemoizedPromiseContext() private static _spawnQueue: Promise = Promise.resolve() @@ -40,7 +40,7 @@ export class SOffice { ) } - get profileDir(): Promise { + get profileDir(): Promise { return this._profileDir.init(async () => await this.setProfileDir()) } @@ -49,7 +49,7 @@ export class SOffice { SOffice._spawnQueue = SOffice._spawnQueue .then(async () => { const spawnArgs = [ - `-env:UserInstallation=${pathToFileURL(await this.profileDir).toString()}`, + `-env:UserInstallation=${(await this.profileDir).fileURL}`, ...args, ] @@ -67,7 +67,7 @@ export class SOffice { const output = data.toString() debug(`[soffice:stderr] %s`, output) - error(`${chalk.yellow`[soffice]`} ${output.trim()}`, { + warn(`${chalk.yellow`[soffice]`} ${output.trim()}`, { singleLine: true, }) }) @@ -88,36 +88,13 @@ export class SOffice { }) } - private async binaryInWSLHost(): Promise { - return !!(await isWSL()) && wslHostMatcher.test(await this.path) - } - - private async setProfileDir(): Promise { - let needToTranslateWindowsPathToWSL = false - - const dir = await (async () => { - // In WSL environment, Marp CLI may use soffice on Windows. If soffice has - // located in host OS (Windows), we have to specify Windows path. - if (await this.binaryInWSLHost()) { - if (wslTmp === undefined) wslTmp = await getWindowsEnv('TMP') - if (wslTmp !== undefined) { - needToTranslateWindowsPathToWSL = true - return path.win32.resolve(wslTmp, this.#profileDirName) - } - } - return path.resolve(os.tmpdir(), this.#profileDirName) - })() - + private async setProfileDir(): Promise { + const dir = path.resolve(os.tmpdir(), this.#profileDirName) debug(`soffice data directory: %s`, dir) - // Ensure the data directory is created - const mkdirPath = needToTranslateWindowsPathToWSL - ? await translateWindowsPathToWSL(dir) - : dir - - await fs.promises.mkdir(mkdirPath, { recursive: true }) - debug(`Created data directory: %s`, mkdirPath) + await fs.promises.mkdir(dir, { recursive: true }) + debug(`soffice data directory created: %s`, dir) - return dir + return { path: dir, fileURL: pathToFileURL(dir).toString() } } }