Skip to content

Commit

Permalink
♻️ getStdOut, getStdOutSync -> execCommand
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffy-g committed Jan 3, 2024
1 parent 401c4a7 commit df5677e
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 70 deletions.
94 changes: 59 additions & 35 deletions cjs/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const cp = require("child_process");
const { execFile, execFileSync } = cp;
/**
* @template T, A, B
* @typedef {unknown extends T ? A : T extends (void | false | undefined) ? A : B} Conditional
* @typedef {void extends T ? A : T extends (void | false | undefined) ? A : B} Conditional
*/
/**
* @template R
Expand All @@ -37,37 +37,40 @@ const { execFile, execFileSync } = cp;
*/
const defaultLocale = "en_US";
/**
* @typedef {cp.ExecFileException} ExecFileException
* @typedef {"defaults" | "locale" | "wmic"} TLocalCmdToken
* @typedef TExecuteCmdOpt
* @prop {true} [async]
* @prop {TLocalCmdToken} command
* @prop {readonly string[]} [args]
*/
/**
* execute command by execFile
*
* more details see {@link https://nodejs.org/api/child_process.html#child_process_child_process_execfile_file_args_options_callback child_process.execFile}
*
* @param {TLocalCmdToken} command
* @param {readonly string[]} [args]
*/
const getStdOut = (command, args) => new Promise((resolve) => {
execFile(command, args /*, execOpt*/, (err, stdout /*, stderr*/) => {
resolve(err || stdout);
});
});
/**
* execute command by execFileSync
*
* more details see {@link https://nodejs.org/api/child_process.html#child_process_child_process_execfilesync_file_args_options child_process.execFileSync}
*
* @param {TLocalCmdToken} command
* @param {readonly string[]} [args]
* @template {TExecuteCmdOpt} P
* @template {Conditional<P["async"], string, Promise<string | ExecFileException>>} R
* @param {P} options
* @returns {R}
* @date 2024-01-03
*/
const getStdOutSync = (command, args) => {
try {
return execFileSync(command, args /*, execOpt*/);
function execCommand(options) {
const { async, command, args } = options;
if (async) {
/** @type {Promise<string | ExecFileException>} */
const p = new Promise((resolve) => {
execFile(command, args, (err, stdout) => {
resolve(err || stdout);
});
});
return /** @type {R} */ (p);
}
catch (e) {
return e;
else {
try {
return /** @type {R} */ (execFileSync(command, args /*, execOpt*/));
}
catch (e) {
return e;
}
}
};
}
/**
* If an exception occurs while executing command such as
* `locale`, `wmic os get locale` the result cannot be applied,
Expand All @@ -77,7 +80,7 @@ const getStdOutSync = (command, args) => {
*
* @todo latest error cache
* @param {string | any} result `string` or `Error` object
* @param {(result: string) => TBD<string>} [processor] If `result` is a `string`, delegate processing
* @param {(result: string) => string} [processor] If `result` is a `string`, delegate processing
*/
function validate(result, processor) {
if (typeof result === "string" && result.length) {
Expand Down Expand Up @@ -123,23 +126,40 @@ exports.purgeExtraToken = pet;
* @param {string} locales result of command `locale -a`
*/
const getSupportedLocale = (locale, locales) => locales.includes(locale) ? locale : /* istanbul ignore next */ defaultLocale;
const [getAppleLocale, getAppleLocaleSync] = /** @type {(a: TLocalCmdToken, b: string[], c: TLocalCmdToken, d: string[]) => TAsyncSyncPair} */ ((cmd0, args0, cmd1, args1) => {
/**
* @typedef {(a: TLocalCmdToken, b: string[], c: TLocalCmdToken, d: string[]) => TAsyncSyncPair} TAppleLocaleFunctions
*/
const [getAppleLocale, getAppleLocaleSync] = /** @type {TAppleLocaleFunctions} */ ((cmd0, args0, cmd1, args1) => {
return [
/**
* Locale detection for MAC OS
* @async
*/
async () => {
const results = await Promise.all([
getStdOut(cmd0, args0).then(ret => validate(ret)),
getStdOut(cmd1, args1).then(ret => validate(ret))
execCommand({
async: true,
command: cmd0,
args: args0
}).then(ret => validate(ret)),
execCommand({
async: true,
command: cmd1,
args: args1
}).then(ret => validate(ret)),
]);
return getSupportedLocale(results[0], results[1]);
},
/**
* Locale detection for MAC OS
*/
() => getSupportedLocale(validate(getStdOutSync(cmd0, args0)), validate(getStdOutSync(cmd1, args1)))
() => getSupportedLocale(validate(execCommand({
command: cmd0,
args: args0
})), validate(execCommand({
command: cmd1,
args: args1
})))
];
})("defaults", ["read", "-globalDomain", "AppleLocale"], "locale", ["-a"]);
const [getUnixLocale, getUnixLocaleSync] = /** @type {(cmd: TLocalCmdToken) => TAsyncSyncPair} */ ((cmd) => {
Expand All @@ -148,11 +168,14 @@ const [getUnixLocale, getUnixLocaleSync] = /** @type {(cmd: TLocalCmdToken) => T
* Locale detection for UNIX OS related
* @async
*/
async () => pet(parseLocale(await getStdOut(cmd).then(ret => validate(ret)))),
async () => pet(parseLocale(await execCommand({
async: true,
command: cmd
}).then(ret => validate(ret)))),
/**
* Locale detection for UNIX OS related
*/
() => pet(parseLocale(validate(getStdOutSync(cmd))))
() => pet(parseLocale(validate(execCommand({ command: cmd }))))
];
})("locale");
/**
Expand All @@ -164,6 +187,7 @@ const parseLCID = (result) => {
return lcid.from(lcidCode) || defaultLocale;
};
const [getWinLocale, getWinLocaleSync] = /** @type {(a: TLocalCmdToken, b: string[]) => TAsyncSyncPair} */ ((cmd0, args0) => {
const opt = { command: cmd0, args: args0 };
return [
/**
* Locale detection for windows OS
Expand All @@ -172,11 +196,11 @@ const [getWinLocale, getWinLocaleSync] = /** @type {(a: TLocalCmdToken, b: strin
*
* @async
*/
async () => validate(await getStdOut(cmd0, args0), parseLCID),
async () => validate(await execCommand(opt), parseLCID),
/**
* Locale detection for windows OS
*/
() => validate(getStdOutSync(cmd0, args0), parseLCID)
() => validate(execCommand(opt), parseLCID)
];
})("wmic", ["os", "get", "locale"]);
/** @type {[ TGetLocaleFunctions<string>, TGetLocaleFunctions<Promise<string>> ]} */
Expand Down
94 changes: 59 additions & 35 deletions esm/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import * as cp from "child_process";
const { execFile, execFileSync } = cp;
/**
* @template T, A, B
* @typedef {unknown extends T ? A : T extends (void | false | undefined) ? A : B} Conditional
* @typedef {void extends T ? A : T extends (void | false | undefined) ? A : B} Conditional
*/
/**
* @template R
Expand All @@ -34,37 +34,40 @@ const { execFile, execFileSync } = cp;
*/
const defaultLocale = "en_US";
/**
* @typedef {cp.ExecFileException} ExecFileException
* @typedef {"defaults" | "locale" | "wmic"} TLocalCmdToken
* @typedef TExecuteCmdOpt
* @prop {true} [async]
* @prop {TLocalCmdToken} command
* @prop {readonly string[]} [args]
*/
/**
* execute command by execFile
*
* more details see {@link https://nodejs.org/api/child_process.html#child_process_child_process_execfile_file_args_options_callback child_process.execFile}
*
* @param {TLocalCmdToken} command
* @param {readonly string[]} [args]
*/
const getStdOut = (command, args) => new Promise((resolve) => {
execFile(command, args /*, execOpt*/, (err, stdout /*, stderr*/) => {
resolve(err || stdout);
});
});
/**
* execute command by execFileSync
*
* more details see {@link https://nodejs.org/api/child_process.html#child_process_child_process_execfilesync_file_args_options child_process.execFileSync}
*
* @param {TLocalCmdToken} command
* @param {readonly string[]} [args]
* @template {TExecuteCmdOpt} P
* @template {Conditional<P["async"], string, Promise<string | ExecFileException>>} R
* @param {P} options
* @returns {R}
* @date 2024-01-03
*/
const getStdOutSync = (command, args) => {
try {
return execFileSync(command, args /*, execOpt*/);
function execCommand(options) {
const { async, command, args } = options;
if (async) {
/** @type {Promise<string | ExecFileException>} */
const p = new Promise((resolve) => {
execFile(command, args, (err, stdout) => {
resolve(err || stdout);
});
});
return /** @type {R} */ (p);
}
catch (e) {
return e;
else {
try {
return /** @type {R} */ (execFileSync(command, args /*, execOpt*/));
}
catch (e) {
return e;
}
}
};
}
/**
* If an exception occurs while executing command such as
* `locale`, `wmic os get locale` the result cannot be applied,
Expand All @@ -74,7 +77,7 @@ const getStdOutSync = (command, args) => {
*
* @todo latest error cache
* @param {string | any} result `string` or `Error` object
* @param {(result: string) => TBD<string>} [processor] If `result` is a `string`, delegate processing
* @param {(result: string) => string} [processor] If `result` is a `string`, delegate processing
*/
function validate(result, processor) {
if (typeof result === "string" && result.length) {
Expand Down Expand Up @@ -120,23 +123,40 @@ export const purgeExtraToken = pet;
* @param {string} locales result of command `locale -a`
*/
const getSupportedLocale = (locale, locales) => locales.includes(locale) ? locale : /* istanbul ignore next */ defaultLocale;
const [getAppleLocale, getAppleLocaleSync] = /** @type {(a: TLocalCmdToken, b: string[], c: TLocalCmdToken, d: string[]) => TAsyncSyncPair} */ ((cmd0, args0, cmd1, args1) => {
/**
* @typedef {(a: TLocalCmdToken, b: string[], c: TLocalCmdToken, d: string[]) => TAsyncSyncPair} TAppleLocaleFunctions
*/
const [getAppleLocale, getAppleLocaleSync] = /** @type {TAppleLocaleFunctions} */ ((cmd0, args0, cmd1, args1) => {
return [
/**
* Locale detection for MAC OS
* @async
*/
async () => {
const results = await Promise.all([
getStdOut(cmd0, args0).then(ret => validate(ret)),
getStdOut(cmd1, args1).then(ret => validate(ret))
execCommand({
async: true,
command: cmd0,
args: args0
}).then(ret => validate(ret)),
execCommand({
async: true,
command: cmd1,
args: args1
}).then(ret => validate(ret)),
]);
return getSupportedLocale(results[0], results[1]);
},
/**
* Locale detection for MAC OS
*/
() => getSupportedLocale(validate(getStdOutSync(cmd0, args0)), validate(getStdOutSync(cmd1, args1)))
() => getSupportedLocale(validate(execCommand({
command: cmd0,
args: args0
})), validate(execCommand({
command: cmd1,
args: args1
})))
];
})("defaults", ["read", "-globalDomain", "AppleLocale"], "locale", ["-a"]);
const [getUnixLocale, getUnixLocaleSync] = /** @type {(cmd: TLocalCmdToken) => TAsyncSyncPair} */ ((cmd) => {
Expand All @@ -145,11 +165,14 @@ const [getUnixLocale, getUnixLocaleSync] = /** @type {(cmd: TLocalCmdToken) => T
* Locale detection for UNIX OS related
* @async
*/
async () => pet(parseLocale(await getStdOut(cmd).then(ret => validate(ret)))),
async () => pet(parseLocale(await execCommand({
async: true,
command: cmd
}).then(ret => validate(ret)))),
/**
* Locale detection for UNIX OS related
*/
() => pet(parseLocale(validate(getStdOutSync(cmd))))
() => pet(parseLocale(validate(execCommand({ command: cmd }))))
];
})("locale");
/**
Expand All @@ -161,6 +184,7 @@ const parseLCID = (result) => {
return lcid.from(lcidCode) || defaultLocale;
};
const [getWinLocale, getWinLocaleSync] = /** @type {(a: TLocalCmdToken, b: string[]) => TAsyncSyncPair} */ ((cmd0, args0) => {
const opt = { command: cmd0, args: args0 };
return [
/**
* Locale detection for windows OS
Expand All @@ -169,11 +193,11 @@ const [getWinLocale, getWinLocaleSync] = /** @type {(a: TLocalCmdToken, b: strin
*
* @async
*/
async () => validate(await getStdOut(cmd0, args0), parseLCID),
async () => validate(await execCommand(opt), parseLCID),
/**
* Locale detection for windows OS
*/
() => validate(getStdOutSync(cmd0, args0), parseLCID)
() => validate(execCommand(opt), parseLCID)
];
})("wmic", ["os", "get", "locale"]);
/** @type {[ TGetLocaleFunctions<string>, TGetLocaleFunctions<Promise<string>> ]} */
Expand Down

0 comments on commit df5677e

Please sign in to comment.