├── .babelrc ├── .editorconfig ├── .gitignore ├── .nvmrc ├── .travis.yml ├── LICENSE ├── README.md ├── package.json └── src ├── cli.js ├── index.js ├── install.js ├── require.js ├── test ├── cli.js ├── index.js ├── install.js ├── require.js └── test-util.js └── util.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015", 4 | "stage-0" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /lib/ 2 | /node_modules/ 3 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 6.2.1 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4" 4 | - "6" 5 | - "7" 6 | script: npm run test 7 | 8 | notifications: 9 | email: 10 | on_success: never 11 | on_failure: always 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Scott Hardy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # npm-install-version 2 | 3 | [![npm-version][npm-version-badge]][npm-version-href] 4 | [![build-status][build-status-badge]][build-status-href] 5 | [![dependencies][dependencies-badge]][dependencies-href] 6 | [![dev-dependencies][dev-dependencies-badge]][dev-dependencies-href] 7 | 8 | 9 | Installs node modules to versioned or custom directories. 10 | 11 | Very useful if you want to use multiple versions of the same package as top-level dependencies. 12 | 13 | 14 | ## CLI Usage 15 | 16 | Install globally: `npm install npm-install-version -g` 17 | 18 | #### Example Usage 19 | 20 | ```text 21 | $ niv csjs@1.0.0 22 | # installs csjs@1.0.0 to node_modules/csjs@1.0.0/ 23 | 24 | $ niv csjs@1.0.0 --destination csjs-v1 25 | # installs csjs@1.0.0 to node_modules/csjs-v1/ 26 | 27 | $ niv scott113341/csjs#some-branch --overwrite 28 | # installs https://github.com/scott113341/csjs#some-branch to node_modules/scott113341-csjs#some-branch/ 29 | # notice how the installation directory is sanitized (the "/" is replaced with a "-") 30 | # overwrites the previously installed version there, which is useful if I just updated "some-branch" 31 | ``` 32 | 33 | #### Full Usage 34 | 35 | ```usage 36 | usage: niv [options...] 37 | 38 | required: 39 | 40 | package 41 | the package to be installed 42 | gets passed directly to "npm install " 43 | 44 | optional: 45 | 46 | --destination, -d 47 | the destination install directory inside node_modules/ 48 | default: sanitized 49 | 50 | --overwrite, -o 51 | overwrite if there is already a package at [destination] 52 | default: false 53 | 54 | --quiet, -q 55 | suppress informational output 56 | default: false 57 | 58 | --help, -h 59 | display this message 60 | ``` 61 | 62 | 63 | ## Programmatic Usage 64 | 65 | Install locally: `npm install npm-install-version --save-dev` 66 | 67 | #### Basic Example 68 | 69 | Let's say we want to benchmark a few versions of `csjs` against each other: 70 | 71 | ```javascript 72 | const niv = require('npm-install-version'); 73 | const benchmark = require('./some-benchmark-function.js'); 74 | 75 | niv.install('csjs@1.0.0'); 76 | // installs csjs@1.0.0 to node_modules/csjs@1.0.0/ 77 | 78 | niv.install('csjs@1.0.1'); 79 | // installs csjs@1.0.1 to node_modules/csjs@1.0.1/ 80 | 81 | const csjs_old = niv.require('csjs@1.0.0'); 82 | const csjs_new = niv.require('csjs@1.0.1'); 83 | // require the old and new versions of csjs 84 | 85 | benchmark([csjs_old, csjs_new], 'some-test-input'); 86 | // run our fake benchmark function on the old and new versions of csjs 87 | ``` 88 | 89 | #### Advanced Example 90 | 91 | ```javascript 92 | const niv = require('npm-install-version'); 93 | 94 | niv.install('csjs@1.0.0', { destination: 'some-dir' }); 95 | // installs csjs@1.0.0 to node_modules/some-dir/ 96 | 97 | niv.install('csjs@1.0.1', { destination: 'some-dir' }); 98 | // doesn't do anything because node_modules/some-dir/ already exists 99 | 100 | niv.install('csjs@1.0.1', { destination: 'some-dir', overwrite: true }); 101 | // installs csjs@1.0.1 to node_modules/some-dir/, overwriting the existing install 102 | ``` 103 | 104 | 105 | [npm-version-badge]: https://img.shields.io/npm/v/npm-install-version.svg?style=flat-square 106 | [npm-version-href]: https://www.npmjs.com/package/npm-install-version 107 | 108 | [build-status-badge]: https://img.shields.io/travis/scott113341/npm-install-version/master.svg?style=flat-square 109 | [build-status-href]: https://travis-ci.org/scott113341/npm-install-version/branches 110 | 111 | [dependencies-badge]: https://img.shields.io/david/scott113341/npm-install-version.svg?style=flat-square 112 | [dependencies-href]: https://david-dm.org/scott113341/npm-install-version#info=dependencies 113 | 114 | [dev-dependencies-badge]: https://img.shields.io/david/dev/scott113341/npm-install-version.svg?style=flat-square 115 | [dev-dependencies-href]: https://david-dm.org/scott113341/npm-install-version#info=devDependencies 116 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "npm-install-version", 3 | "version": "6.0.2", 4 | "description": "Installs node modules to versioned or custom directories.", 5 | "author": "Scott Hardy", 6 | "license": "MIT", 7 | "repository": "git@github.com:scott113341/npm-install-version.git", 8 | "homepage": "https://github.com/scott113341/npm-install-version", 9 | "bugs": "https://github.com/scott113341/npm-install-version/issues", 10 | "keywords": [ 11 | "npm", 12 | "install", 13 | "multiple", 14 | "versions" 15 | ], 16 | "main": "lib/index.js", 17 | "files": [ 18 | "lib/", 19 | "src/" 20 | ], 21 | "bin": { 22 | "niv": "lib/cli.js", 23 | "npm-install-version": "lib/cli.js" 24 | }, 25 | "scripts": { 26 | "build": "shx rm -rf lib/ && shx mkdir lib/ && babel --copy-files --source-maps --out-dir lib/ src/", 27 | "lint": "semistandard --fix", 28 | "postinstall": "postinstall-build lib/ \"npm run build\"", 29 | "test": "npm run build && node lib/test/index.js" 30 | }, 31 | "dependencies": { 32 | "deasync": "^0.1.9", 33 | "minimist": "^1.2.0", 34 | "npm": "^4.0.3", 35 | "postinstall-build": "2.1.3", 36 | "sanitize-filename": "^1.6.1", 37 | "shelljs": "^0.7.5" 38 | }, 39 | "devDependencies": { 40 | "babel-cli": "^6.18.0", 41 | "babel-preset-es2015": "^6.18.0", 42 | "babel-preset-stage-0": "^6.16.0", 43 | "np": "^2.11.0", 44 | "semistandard": "^9.2.1", 45 | "shx": "^0.2.1", 46 | "tape": "^4.6.3" 47 | }, 48 | "semistandard": { 49 | "ignore": [ 50 | "/lib/" 51 | ] 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const minimist = require('minimist'); 4 | const niv = require('./index.js'); 5 | const { getUsage } = require('./util.js'); 6 | 7 | const options = { 8 | default: { 9 | destination: undefined, 10 | cmd: undefined, 11 | help: false, 12 | overwrite: undefined, 13 | quiet: false 14 | }, 15 | alias: { 16 | destination: 'd', 17 | cmd: 'c', 18 | help: 'h', 19 | overwrite: 'o', 20 | quiet: 'q' 21 | } 22 | }; 23 | 24 | const args = minimist(process.argv.slice(2), options); 25 | 26 | if (args.help) { 27 | process.stdout.write(getUsage()); 28 | process.exit(0); 29 | } 30 | if (!args._.length) { 31 | process.stderr.write(getUsage()); 32 | process.exit(1); 33 | } 34 | 35 | niv.install(args._[0], args); 36 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | install: require('./install.js'), 3 | require: require('./require.js') 4 | }; 5 | -------------------------------------------------------------------------------- /src/install.js: -------------------------------------------------------------------------------- 1 | const childProcess = require('child_process'); 2 | const path = require('path'); 3 | const shelljs = require('shelljs'); 4 | 5 | const util = require('./util.js'); 6 | 7 | const CWD = process.cwd(); 8 | const TEMP = path.join(CWD, 'node_modules', '.npm-install-version-temp' + Math.random()); 9 | 10 | function install (npmPackage, options = {}) { 11 | const { 12 | destination = util.sanitize(npmPackage), 13 | overwrite = false, 14 | quiet = false 15 | } = options; 16 | 17 | const log = quiet ? () => {} : (...args) => console.log(...args); 18 | 19 | if (!npmPackage) util.error(); 20 | const destinationPath = path.join(CWD, 'node_modules', destination); 21 | if (!overwrite && util.directoryExists(destinationPath)) { 22 | return log(`Directory at ${destinationPath} already exists, skipping`); 23 | } 24 | 25 | let errored = false; 26 | try { 27 | // make temp install dir 28 | shelljs.rm('-rf', TEMP); 29 | shelljs.mkdir('-p', path.join(TEMP, 'node_modules')); 30 | 31 | // copy local .npmrc file if exists 32 | const npmrcFile = path.join(CWD, '.npmrc'); 33 | if (shelljs.test('-f', npmrcFile)) { 34 | shelljs.cp(npmrcFile, TEMP); 35 | } 36 | 37 | // install package to temp dir 38 | const installOptions = { 39 | cwd: TEMP, 40 | stdio: [null, null, null] 41 | }; 42 | const command = process.platform === 'win32' ? 'npm.cmd' : 'npm'; 43 | childProcess.spawnSync(command, ['install', npmPackage], installOptions); 44 | 45 | // get real package name 46 | const packageName = util.getPackageName(npmPackage); 47 | 48 | // move deps inside package 49 | shelljs.mkdir('-p', path.join(TEMP, 'node_modules', packageName, 'node_modules')); 50 | shelljs.ls(path.join(TEMP, 'node_modules')) 51 | .forEach(dep => { 52 | if (dep === packageName) return; 53 | const from = path.join(TEMP, 'node_modules', dep).toString(); 54 | const to = path.join(TEMP, 'node_modules', packageName, 'node_modules', dep).toString(); 55 | shelljs.mv(from, to); 56 | }); 57 | 58 | // copy to node_modules/ 59 | shelljs.rm('-rf', destinationPath); 60 | shelljs.mv(path.join(TEMP, 'node_modules', packageName), destinationPath); 61 | 62 | log(`Installed ${npmPackage} to ${destinationPath}`); 63 | } catch (err) { 64 | errored = true; 65 | console.error(`Error installing ${npmPackage}`); 66 | console.error(err.toString()); 67 | } finally { 68 | // clean up temp install dir 69 | shelljs.rm('-rf', TEMP); 70 | 71 | if (errored) process.exit(1); 72 | } 73 | } 74 | 75 | module.exports = install; 76 | -------------------------------------------------------------------------------- /src/require.js: -------------------------------------------------------------------------------- 1 | const { sanitize } = require('./util.js'); 2 | 3 | function _require (npmPackage) { 4 | return require(sanitize(npmPackage)); 5 | } 6 | 7 | module.exports = _require; 8 | -------------------------------------------------------------------------------- /src/test/cli.js: -------------------------------------------------------------------------------- 1 | const spawnSync = require('child_process').spawnSync; 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | const test = require('tape'); 5 | 6 | const { clean } = require('./test-util.js'); 7 | 8 | const CLI_PATH = path.join(__dirname, '..', 'cli.js'); 9 | function run (...commands) { 10 | spawnSync(CLI_PATH, commands, { stdio: [0, 1, 2] }); 11 | } 12 | 13 | test('cli install normal', t => { 14 | clean(); 15 | run('csjs@1.0.0'); 16 | const packageJson = fs.readFileSync('node_modules/csjs@1.0.0/package.json'); 17 | t.equal(JSON.parse(packageJson).version, '1.0.0'); 18 | t.end(); 19 | }); 20 | 21 | test('cli install remote', t => { 22 | clean(); 23 | run('scott113341/csjs#extract-extends-performance'); 24 | const packageJson = fs.readFileSync('node_modules/scott113341-csjs#extract-extends-performance/package.json'); 25 | t.equal(JSON.parse(packageJson).version, '1.0.4'); 26 | t.end(); 27 | }); 28 | 29 | test('cli install scoped', t => { 30 | clean(); 31 | run('@scott113341/niv-scoped-test@1.0.0'); 32 | const packageJson = fs.readFileSync('node_modules/@scott113341-niv-scoped-test@1.0.0/package.json'); 33 | t.equal(JSON.parse(packageJson).version, '1.0.0'); 34 | t.end(); 35 | }); 36 | 37 | test('cli install w/ dependencies', t => { 38 | clean(); 39 | run('push-dir@0.2.2'); 40 | const packageJson = fs.readFileSync('node_modules/push-dir@0.2.2/package.json'); 41 | t.equal(JSON.parse(packageJson).version, '0.2.2'); 42 | t.end(); 43 | }); 44 | 45 | test('cli install w/ destination', t => { 46 | clean(); 47 | run('csjs@1.0.0', '--destination=csjs@yolo'); 48 | const packageJson = fs.readFileSync('node_modules/csjs@yolo/package.json'); 49 | t.equal(JSON.parse(packageJson).version, '1.0.0'); 50 | t.end(); 51 | }); 52 | 53 | test('cli install w/o overwrite', t => { 54 | clean(); 55 | run('csjs@1.0.0', '--destination=csjs@yolo'); 56 | const packageJson1 = fs.readFileSync('node_modules/csjs@yolo/package.json'); 57 | t.equal(JSON.parse(packageJson1).version, '1.0.0'); 58 | 59 | run('csjs@1.0.1', '--destination=csjs@yolo'); 60 | const packageJson2 = fs.readFileSync('node_modules/csjs@yolo/package.json'); 61 | t.equal(JSON.parse(packageJson2).version, '1.0.0'); 62 | 63 | t.end(); 64 | }); 65 | 66 | test('cli install w/ overwrite', t => { 67 | clean(); 68 | run('csjs@1.0.0', '--destination=csjs@yolo'); 69 | const packageJson1 = fs.readFileSync('node_modules/csjs@yolo/package.json'); 70 | t.equal(JSON.parse(packageJson1).version, '1.0.0'); 71 | 72 | run('csjs@1.0.1', '--destination=csjs@yolo', '--overwrite'); 73 | const packageJson2 = fs.readFileSync('node_modules/csjs@yolo/package.json'); 74 | t.equal(JSON.parse(packageJson2).version, '1.0.1'); 75 | 76 | t.end(); 77 | }); 78 | 79 | test('cli help', t => { 80 | clean(); 81 | const out = spawnSync(CLI_PATH, ['--help']); 82 | const stdout = out.stdout.toString(); 83 | t.equal(out.status, 0); 84 | t.equal(stdout.indexOf('usage: niv [options...]'), 0); 85 | t.equal(stdout.length > 100, true); 86 | t.end(); 87 | }); 88 | 89 | test('cli no package', t => { 90 | clean(); 91 | const out = spawnSync(CLI_PATH); 92 | const stderr = out.stderr.toString(); 93 | t.equal(out.status, 1); 94 | t.equal(stderr.indexOf('usage: niv [options...]'), 0); 95 | t.equal(stderr.length > 100, true); 96 | t.end(); 97 | }); 98 | -------------------------------------------------------------------------------- /src/test/index.js: -------------------------------------------------------------------------------- 1 | require('./cli.js'); 2 | require('./install.js'); 3 | require('./require.js'); 4 | -------------------------------------------------------------------------------- /src/test/install.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const test = require('tape'); 3 | 4 | const niv = require('../index.js'); 5 | const { clean } = require('./test-util.js'); 6 | 7 | test('niv.install normal', t => { 8 | clean(); 9 | niv.install('csjs@1.0.0'); 10 | const packageJson = fs.readFileSync('node_modules/csjs@1.0.0/package.json'); 11 | t.equal(JSON.parse(packageJson).version, '1.0.0'); 12 | t.end(); 13 | }); 14 | 15 | test('niv.install remote', t => { 16 | clean(); 17 | niv.install('scott113341/csjs#extract-extends-performance'); 18 | const packageJson = fs.readFileSync('node_modules/scott113341-csjs#extract-extends-performance/package.json'); 19 | t.equal(JSON.parse(packageJson).version, '1.0.4'); 20 | t.end(); 21 | }); 22 | 23 | test('niv.install scoped', t => { 24 | clean(); 25 | niv.install('@scott113341/niv-scoped-test@1.0.0'); 26 | const packageJson = fs.readFileSync('node_modules/@scott113341-niv-scoped-test@1.0.0/package.json'); 27 | t.equal(JSON.parse(packageJson).version, '1.0.0'); 28 | t.end(); 29 | }); 30 | 31 | test('niv.install w/ dependencies', t => { 32 | clean(); 33 | niv.install('push-dir@0.2.2'); 34 | const packageJson = fs.readFileSync('node_modules/push-dir@0.2.2/package.json'); 35 | t.equal(JSON.parse(packageJson).version, '0.2.2'); 36 | t.end(); 37 | }); 38 | 39 | test('niv.install w/ destination', t => { 40 | clean(); 41 | niv.install('csjs@1.0.0', { destination: 'csjs@yolo' }); 42 | const packageJson = fs.readFileSync('node_modules/csjs@yolo/package.json'); 43 | t.equal(JSON.parse(packageJson).version, '1.0.0'); 44 | t.end(); 45 | }); 46 | 47 | test('niv.install w/o overwrite', t => { 48 | clean(); 49 | niv.install('csjs@1.0.0', { destination: 'csjs@yolo' }); 50 | const packageJson1 = fs.readFileSync('node_modules/csjs@yolo/package.json'); 51 | t.equal(JSON.parse(packageJson1).version, '1.0.0'); 52 | 53 | niv.install('csjs@1.0.1', { destination: 'csjs@yolo' }); 54 | const packageJson2 = fs.readFileSync('node_modules/csjs@yolo/package.json'); 55 | t.equal(JSON.parse(packageJson2).version, '1.0.0'); 56 | 57 | t.end(); 58 | }); 59 | 60 | test('niv.install w/ overwrite', t => { 61 | clean(); 62 | niv.install('csjs@1.0.0', { destination: 'csjs@yolo' }); 63 | const packageJson1 = fs.readFileSync('node_modules/csjs@yolo/package.json'); 64 | t.equal(JSON.parse(packageJson1).version, '1.0.0'); 65 | 66 | niv.install('csjs@1.0.1', { destination: 'csjs@yolo', overwrite: true }); 67 | const packageJson2 = fs.readFileSync('node_modules/csjs@yolo/package.json'); 68 | t.equal(JSON.parse(packageJson2).version, '1.0.1'); 69 | 70 | t.end(); 71 | }); 72 | -------------------------------------------------------------------------------- /src/test/require.js: -------------------------------------------------------------------------------- 1 | const test = require('tape'); 2 | 3 | const niv = require('../index.js'); 4 | const { clean } = require('./test-util.js'); 5 | 6 | test('niv.require normal', t => { 7 | clean(); 8 | niv.install('csjs@1.0.0'); 9 | const csjs = niv.require('csjs@1.0.0'); 10 | t.assert(typeof csjs, 'function'); 11 | t.end(); 12 | }); 13 | 14 | test('niv.require remote', t => { 15 | clean(); 16 | niv.install('scott113341/csjs#extract-extends-performance'); 17 | const csjs = niv.require('scott113341/csjs#extract-extends-performance'); 18 | t.assert(typeof csjs, 'function'); 19 | t.end(); 20 | }); 21 | 22 | test('niv.require scoped', t => { 23 | clean(); 24 | niv.install('@scott113341/niv-scoped-test@1.0.0'); 25 | const pkg = niv.require('@scott113341/niv-scoped-test@1.0.0'); 26 | t.assert(typeof pkg.addNumbers, 'function'); 27 | t.end(); 28 | }); 29 | 30 | test('niv.require w/ destination', t => { 31 | clean(); 32 | niv.install('scott113341/csjs#extract-extends-performance', { destination: 'csjs@yolo' }); 33 | const csjs = niv.require('csjs@yolo'); 34 | t.assert(typeof csjs, 'function'); 35 | t.end(); 36 | }); 37 | -------------------------------------------------------------------------------- /src/test/test-util.js: -------------------------------------------------------------------------------- 1 | const shelljs = require('shelljs'); 2 | 3 | function clean () { 4 | shelljs.rm('-rf', 'node_modules/csjs*', 'node_modules/@scott113341*', 'node_modules/scott113341*', 'node_modules/push-dir*'); 5 | } 6 | 7 | module.exports = { 8 | clean 9 | }; 10 | -------------------------------------------------------------------------------- /src/util.js: -------------------------------------------------------------------------------- 1 | const deasync = require('deasync'); 2 | const fs = require('fs'); 3 | const npm = require('npm'); 4 | const path = require('path'); 5 | const sanitizeFilename = require('sanitize-filename'); 6 | 7 | function directoryExists (destination) { 8 | try { 9 | fs.lstatSync(destination); 10 | return true; 11 | } catch (e) { 12 | return false; 13 | } 14 | } 15 | 16 | function error () { 17 | throw Error('You must specify an install target like this: csjs@1.0.0'); 18 | } 19 | 20 | function getPackageName (packageName) { 21 | const load = deasync(npm.load); 22 | load({ loaded: false }); 23 | 24 | const fetchPackageMetadata = deasync(require('npm/lib/fetch-package-metadata.js')); 25 | return fetchPackageMetadata(packageName, process.cwd()).name; 26 | } 27 | 28 | function getUsage () { 29 | var readme = path.join(__dirname, '..', 'README.md'); 30 | var readmeText = String(fs.readFileSync(readme)); 31 | return /```usage\n([\s\S]*?)```/.exec(readmeText)[1]; 32 | } 33 | 34 | function sanitize (npmPackage) { 35 | return sanitizeFilename(npmPackage, { replacement: '-' }); 36 | } 37 | 38 | module.exports = { 39 | directoryExists, 40 | error, 41 | getPackageName, 42 | getUsage, 43 | sanitize 44 | }; 45 | --------------------------------------------------------------------------------