|
1 |
| -import * as Fs from 'fs' |
2 |
| -import * as Path from 'path' |
| 1 | +import * as fs from 'fs' |
| 2 | +import { basename, join } from 'path' |
| 3 | +import { promisify } from 'util' |
3 | 4 |
|
4 | 5 | import * as ArrayUtil from './util/array'
|
5 | 6 | import * as FsUtil from './util/fs'
|
6 | 7 | import * as ShUtil from './util/sh'
|
7 | 8 |
|
| 9 | +const lstatAsync = promisify(fs.lstat) |
| 10 | +const readdirAsync = promisify(fs.readdir) |
| 11 | + |
8 | 12 | /**
|
9 | 13 | * Provides information based on the programs on your PATH
|
10 | 14 | */
|
@@ -58,34 +62,40 @@ export default class Executables {
|
58 | 62 | /**
|
59 | 63 | * Only returns direct children, or the path itself if it's an executable.
|
60 | 64 | */
|
61 |
| -function findExecutablesInPath(path: string): Promise<string[]> { |
| 65 | +async function findExecutablesInPath(path: string): Promise<string[]> { |
62 | 66 | path = FsUtil.untildify(path)
|
63 |
| - return new Promise((resolve, _) => { |
64 |
| - Fs.lstat(path, (err, stat) => { |
65 |
| - if (err) { |
66 |
| - resolve([]) |
67 |
| - } else { |
68 |
| - if (stat.isDirectory()) { |
69 |
| - Fs.readdir(path, (readDirErr, paths) => { |
70 |
| - if (readDirErr) { |
71 |
| - resolve([]) |
72 |
| - } else { |
73 |
| - const files = paths.map(p => |
74 |
| - FsUtil.getStats(Path.join(path, p)) |
75 |
| - .then(s => (s.isFile() ? [Path.basename(p)] : [])) |
76 |
| - .catch(() => []), |
77 |
| - ) |
78 |
| - |
79 |
| - resolve(Promise.all(files).then(ArrayUtil.flatten)) |
80 |
| - } |
81 |
| - }) |
82 |
| - } else if (stat.isFile()) { |
83 |
| - resolve([Path.basename(path)]) |
84 |
| - } else { |
85 |
| - // Something else. |
86 |
| - resolve([]) |
| 67 | + |
| 68 | + try { |
| 69 | + const pathStats = await lstatAsync(path) |
| 70 | + |
| 71 | + if (pathStats.isDirectory()) { |
| 72 | + const childrenPaths = await readdirAsync(path) |
| 73 | + |
| 74 | + const files = [] |
| 75 | + |
| 76 | + for (const childrenPath of childrenPaths) { |
| 77 | + try { |
| 78 | + const stats = await lstatAsync(join(path, childrenPath)) |
| 79 | + if (isExecutableFile(stats)) { |
| 80 | + files.push(basename(childrenPath)) |
| 81 | + } |
| 82 | + } catch (error) { |
| 83 | + // Ignore error |
87 | 84 | }
|
88 | 85 | }
|
89 |
| - }) |
90 |
| - }) |
| 86 | + |
| 87 | + return files |
| 88 | + } else if (isExecutableFile(pathStats)) { |
| 89 | + return [basename(path)] |
| 90 | + } |
| 91 | + } catch (error) { |
| 92 | + // Ignore error |
| 93 | + } |
| 94 | + |
| 95 | + return [] |
| 96 | +} |
| 97 | + |
| 98 | +function isExecutableFile(stats: fs.Stats): boolean { |
| 99 | + const isExecutable = !!(1 & parseInt((stats.mode & parseInt('777', 8)).toString(8)[0])) |
| 100 | + return stats.isFile() && isExecutable |
91 | 101 | }
|
0 commit comments