-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
notable change: - break: use `getDefaultOpenCommandList` instead of `getDefaultOpen` - add: `ResolveCommand` moved from `@dr-js/node`
- Loading branch information
Showing
8 changed files
with
127 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,32 @@ | ||
const DEFAULT_OPEN_MAP = { | ||
linux: 'xdg-open', | ||
win32: 'start', | ||
darwin: 'open', | ||
android: 'termux-open' // TODO: may have other options? | ||
const DEFAULT_OPEN_COMMAND_LIST_MAP = { | ||
// [ process.platform ]: [ command, ...prefixArgList ] | ||
|
||
linux: [ 'xdg-open' ], | ||
|
||
// better than the both `start` (cmd builtin need nasty quoting) and `explorer.exe` (standalone but fail on `http://localhost?a=1#some://url?with=special&chars=:->`) | ||
// now the problem will be how to pass this to node on win32. check quoting problem: https://github.com/sindresorhus/open#double-quotes-on-windows | ||
// check: | ||
// - https://ss64.com/nt/rundll32.html | ||
// - https://superuser.com/questions/1456314/open-url-with-windows-explorer/1456333#1456333 | ||
// - https://superuser.com/questions/36728/can-i-launch-urls-from-command-line-in-windows/36730#36730 | ||
win32: [ 'rundll32.exe', 'url.dll,OpenURL' ], | ||
|
||
darwin: [ 'open' ], | ||
|
||
android: [ 'termux-open' ] // TODO: may have other options? | ||
} | ||
|
||
// open Path or File with System Default | ||
const getDefaultOpen = () => { | ||
const defaultOpen = DEFAULT_OPEN_MAP[ process.platform ] | ||
if (!defaultOpen) throw new Error(`unsupported platform: ${process.platform}`) | ||
return defaultOpen | ||
// open URL or File with System Default, no need `{ shell: true }` os no extra escaping needed | ||
const getDefaultOpenCommandList = () => { | ||
const defaultOpenCommandList = DEFAULT_OPEN_COMMAND_LIST_MAP[ process.platform ] | ||
if (!defaultOpenCommandList) throw new Error(`unsupported platform: ${process.platform}`) | ||
return defaultOpenCommandList // [ command, ...prefixArgList ] | ||
} | ||
|
||
export { getDefaultOpen } | ||
export { getDefaultOpenCommandList } | ||
|
||
// ultimate URL test: | ||
// require('child_process').spawnSync('rundll32.exe', [ 'url.dll,OpenURL', 'http://localhost?a=1#some://url?with=special&chars\'\":[]{}()!@#$%^&*-=_+<>,.?/\\' ]) | ||
// require('child_process').spawnSync('open', [ 'http://localhost?a=1#some://url?with=special&chars\'\":[]{}()!@#$%^&*-=_+<>,.?/\\' ]) | ||
// require('child_process').spawnSync('xdg-open', [ 'http://localhost?a=1#some://url?with=special&chars\'\":[]{}()!@#$%^&*-=_+<>,.?/\\' ]) | ||
// require('child_process').spawnSync('termux-open', [ 'http://localhost?a=1#some://url?with=special&chars\'\":[]{}()!@#$%^&*-=_+<>,.?/\\' ]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { spawnSync } from 'child_process' | ||
import { resolve } from 'path' | ||
import { run } from './Run' | ||
|
||
const configureWin32 = (extList = (process.env.PATHEXT || '.EXE;.BAT;.CMD').toUpperCase().split(';')) => [ // process.env.PATHEXT // '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC' | ||
'where.exe', // search cwd // https://ss64.com/nt/where.html | ||
(stdoutString) => stdoutString.split('\r\n').find((path) => extList.includes(path.slice(path.lastIndexOf('.')).toUpperCase())) || '' | ||
] | ||
const configureLinux = () => [ | ||
'which', // do not search cwd // https://ss64.com/bash/which.html | ||
(stdoutString) => stdoutString.trim() | ||
] | ||
|
||
const CONFIGURE_MAP = { | ||
linux: configureLinux, | ||
win32: configureWin32, | ||
darwin: configureLinux, | ||
android: configureLinux | ||
} | ||
|
||
let cacheConfigure | ||
const configureCached = () => { | ||
if (cacheConfigure === undefined) { | ||
const configure = CONFIGURE_MAP[ process.platform ] | ||
if (!configure) throw new Error(`unsupported platform: ${process.platform}`) | ||
cacheConfigure = configure() | ||
} | ||
return cacheConfigure | ||
} | ||
|
||
const resolveCommandName = (commandName, cwd) => { // if not found, result in empty string: "" | ||
const [ checkCommand, processFunc ] = configureCached() | ||
return processFunc(String(spawnSync(checkCommand, [ commandName ], { cwd }).stdout || '')) | ||
} | ||
|
||
const resolveCommandNameAsync = async (commandName, cwd) => { // if not found, result in empty string: "" | ||
const [ checkCommand, processFunc ] = configureCached() | ||
const { promise, stdoutPromise } = run({ command: checkCommand, argList: [ commandName ], option: { cwd }, quiet: true }) | ||
return processFunc(String(await promise.then(() => stdoutPromise, () => ''))) | ||
} | ||
|
||
// try resolve command, fast resolve if command itself contain path sep | ||
const resolveCommand = (command, cwd) => REGEXP_PATH_SEP.test(command) ? resolve(cwd || '', command) : resolveCommandName(command, cwd) | ||
const resolveCommandAsync = async (command, cwd) => REGEXP_PATH_SEP.test(command) ? resolve(cwd || '', command) : resolveCommandNameAsync(command, cwd) | ||
const REGEXP_PATH_SEP = /[\\/]/ | ||
|
||
export { | ||
resolveCommandName, resolveCommandNameAsync, | ||
resolveCommand, resolveCommandAsync | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { strictEqual } from 'source/common/verify' | ||
import { | ||
resolveCommandName, | ||
resolveCommandNameAsync | ||
} from './ResolveCommand' | ||
|
||
const { describe, it, info = console.log } = global | ||
|
||
describe('Node.Module.ResolveCommand', () => { | ||
const COMMAND_NAME_LIST = [ | ||
// [ commandName, isExpectResult ] | ||
[ process.platform === 'win32' ? 'ipconfig' : 'ifconfig', true ], | ||
[ 'npm', true ], | ||
[ 'npx', true ], | ||
[ 'node', true ], | ||
[ 'git', true ], | ||
[ 'tar', true ], | ||
[ 'noop-0123456789', false ] // non-exist commandName should return "" | ||
] | ||
|
||
it('resolveCommandName()', () => { | ||
for (const [ commandName, isExpectResult ] of COMMAND_NAME_LIST) { | ||
const result = resolveCommandName(commandName) | ||
info(`${JSON.stringify(commandName)} => ${JSON.stringify(result)}`) | ||
strictEqual(Boolean(result), isExpectResult) | ||
} | ||
}) | ||
|
||
it('resolveCommandNameAsync()', async () => { | ||
for (const [ commandName, isExpectResult ] of COMMAND_NAME_LIST) { | ||
const result = await resolveCommandNameAsync(commandName) | ||
info(`${JSON.stringify(commandName)} => ${JSON.stringify(result)}`) | ||
strictEqual(Boolean(result), isExpectResult) | ||
} | ||
}) | ||
}) |