-
Notifications
You must be signed in to change notification settings - Fork 456
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use
mdfind
command to get all Applications + refactoring initializers
Now plugins could have two fields: initialize and initializeAsync. First will be called in the same process, and initializeAsync will be called in background window using RPC (like in contacts plugin) Also apps list now pre-fetched after app start
- Loading branch information
Showing
8 changed files
with
162 additions
and
47 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,45 @@ | ||
import glob from 'glob'; | ||
import flatten from 'lodash/flatten'; | ||
import mdfind from 'lib/mdfind'; | ||
|
||
// Patters that we use for searching files | ||
const PATTERNS = [ | ||
// Apps in root applications folder | ||
'/Applications/*.app', | ||
// Apps inside other apps | ||
'/Applications/*.app/Contents/Applications/*.app', | ||
// Apps in folders | ||
'/Applications/!(*.app)/**.app', | ||
// System preferences | ||
'/System/Library/PreferencePanes/*.prefPane', | ||
|
||
/** | ||
* List of supported files | ||
* @type {Array} | ||
*/ | ||
const supportedTypes = [ | ||
'com.apple.application-bundle', | ||
'com.apple.systempreference.prefpane' | ||
]; | ||
|
||
/** | ||
* Promise-wrapper for glob function | ||
* @param {String} pattern Pattern for glob function | ||
* @param {Object} options | ||
* @return {Promise} | ||
* Build mdfind query | ||
* | ||
* @return {String} | ||
*/ | ||
const globPromise = (pattern, options) => new Promise((resolve, reject) => { | ||
glob(pattern, options, (err, files) => { | ||
if (err) return reject(err); | ||
resolve(files); | ||
}); | ||
}); | ||
const buildQuery = () => { | ||
return supportedTypes.map(type => `kMDItemContentType=${type}`).join('||'); | ||
} | ||
|
||
/** | ||
* Function to terminate previous query | ||
* | ||
* @return {Function} | ||
*/ | ||
let cancelPrevious = () => {}; | ||
|
||
/** | ||
* Get list of all installed applications | ||
* @return {Promise<Array>} | ||
*/ | ||
export default function () { | ||
return Promise.all(PATTERNS.map(pattern => globPromise(pattern))) | ||
.then(files => { | ||
return flatten(files).map(path => ({ | ||
path, | ||
name: path.match(/\/([^\/]+)\.(app|prefPane)$/i)[1], | ||
})); | ||
export default (term) => { | ||
console.log('Get new apps list') | ||
cancelPrevious(); | ||
return new Promise((resolve, reject) => { | ||
const { output, terminate } = mdfind({ | ||
query: buildQuery() | ||
}); | ||
cancelPrevious = terminate; | ||
const result = []; | ||
output.on('data', (file) => result.push(file)); | ||
output.on('end', () => resolve(result)); | ||
}); | ||
} |
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 |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import _ from 'lodash'; | ||
import { spawn } from 'child_process'; | ||
import { map, split, through } from 'event-stream'; | ||
|
||
const REAL_KEYS = { | ||
'kMDItemDisplayName': 'name', | ||
'kMDItemLastUsedDate': 'lastUsed' | ||
} | ||
|
||
/** | ||
* Parse mdfind result line to JS object | ||
* | ||
* @param {String} line | ||
* @return {Object} | ||
*/ | ||
function parseLine(line) { | ||
const attrs = line.split(' '); | ||
const result = { | ||
// First attr is always full path to the item | ||
path: attrs.shift() | ||
} | ||
attrs.forEach(attr => { | ||
const [key, value] = attr.split(' = '); | ||
result[REAL_KEYS[key] || key] = getValue(value); | ||
}); | ||
this.emit('data', result); | ||
} | ||
|
||
const getValue = (item) => { | ||
if (item === '(null)') { | ||
return null; | ||
} else if (_.startsWith(item, '(\n "') && _.endsWith(item, '"\n)')) { | ||
const actual = item.slice(7, -3); | ||
const lines = actual.split('",\n "'); | ||
return lines; | ||
} else { | ||
return item; | ||
} | ||
} | ||
|
||
const filterEmpty = (data, done) => { | ||
if (data === '') { | ||
done(); | ||
} else { | ||
done(null, data); | ||
} | ||
} | ||
|
||
const makeArgs = (array, argName) => { | ||
return _.flatten(array.map(item => [argName, item])); | ||
} | ||
|
||
export default function mdfind ({query, attributes = ['kMDItemDisplayName', 'kMDItemLastUsedDate'], names = [], directories = [], live = false, interpret = false, limit} = {}) { | ||
const dirArgs = makeArgs(directories, '-onlyin') | ||
const nameArgs = makeArgs(names, '-name') | ||
const attrArgs = makeArgs(attributes, '-attr') | ||
const interpretArgs = interpret ? ['-interpret'] : [] | ||
const queryArgs = query ? [query] : [] | ||
|
||
const args = ['-0'].concat(dirArgs, nameArgs, attrArgs, interpretArgs, live ? ['-live', '-reprint'] : [], queryArgs) | ||
|
||
const child = spawn('mdfind', args) | ||
|
||
let times = 0 | ||
|
||
return { | ||
output: child.stdout | ||
.pipe(split('\0')) | ||
.pipe(map(filterEmpty)) | ||
.pipe(through(function (data) { | ||
times++ | ||
if (limit && times === limit) child.kill() | ||
if (limit && times > limit) return | ||
this.queue(data) | ||
})) | ||
.pipe(through(parseLine)), | ||
terminate: () => child.kill() | ||
} | ||
} |
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
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