├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .github ├── banner.svg └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── jest.config.js ├── package.json ├── renovate.json ├── src ├── index.js ├── package-manager.js ├── package-managers │ ├── index.js │ ├── nop.js │ ├── npm.js │ └── yarn.js └── workspace.js ├── test ├── detect.test.js ├── fixtures │ ├── npm │ │ ├── package-lock.json │ │ └── package.json │ ├── npm_fallback │ │ └── package.json │ ├── yarn │ │ ├── package.json │ │ └── yarn.lock │ └── yarn_and_npm │ │ ├── package-lock.json │ │ ├── package.json │ │ └── yarn.lock ├── grant.test.js ├── install.test.js └── lmify.test.js ├── types └── lmify.d.ts └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "targets": { 7 | "node": "current" 8 | } 9 | } 10 | ] 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_size = 2 6 | indent_style = space 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | '@nuxtjs' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | ci: 13 | runs-on: ${{ matrix.os }} 14 | 15 | strategy: 16 | matrix: 17 | os: [ubuntu-latest, macos-latest, windows-latest] 18 | node: [10, 12] 19 | 20 | steps: 21 | - uses: actions/setup-node@v3 22 | with: 23 | node-version: ${{ matrix.node }} 24 | 25 | - name: checkout 26 | uses: actions/checkout@master 27 | 28 | - name: cache node_modules 29 | uses: actions/cache@v1 30 | with: 31 | path: node_modules 32 | key: ${{ matrix.os }}-node-v${{ matrix.node }}-deps-${{ hashFiles(format('{0}{1}', github.workspace, '/yarn.lock')) }} 33 | 34 | - name: Install dependencies 35 | if: steps.cache.outputs.cache-hit != 'true' 36 | run: yarn 37 | 38 | - name: Lint 39 | run: yarn lint 40 | 41 | - name: Test 42 | run: yarn test 43 | 44 | - name: Coverage 45 | uses: codecov/codecov-action@v1 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | dist 4 | .nyc_output 5 | .vscode 6 | *.iml 7 | .idea 8 | *.DS_Store 9 | coverage 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ## [0.3.0](https://github.com/nuxt/lmify/compare/v0.2.2...v0.3.0) (2020-04-10) 6 | 7 | 8 | ### Features 9 | 10 | * Add fallback for npm and add test for yarn priority ([3b49d82](https://github.com/nuxt/lmify/commit/3b49d82eb537c24eeb88cda7055fcc4438ecca45)) 11 | 12 | ### [0.2.2](https://github.com/nuxt/lmify/compare/v0.2.1...v0.2.2) (2019-06-04) 13 | 14 | 15 | ### Bug Fixes 16 | 17 | * **ts:** fix type export configuration ([#10](https://github.com/nuxt/lmify/issues/10)) ([0ad374c](https://github.com/nuxt/lmify/commit/0ad374c)) 18 | 19 | 20 | 21 | ### [0.2.1](https://github.com/nuxt/lmify/compare/v0.2.0...v0.2.1) (2019-06-04) 22 | 23 | 24 | ### Bug Fixes 25 | 26 | * add types to package files ([90d67a2](https://github.com/nuxt/lmify/commit/90d67a2)) 27 | 28 | 29 | 30 | ## [0.2.0](https://github.com/nuxt/lmify/compare/v0.1.0...v0.2.0) (2019-06-04) 31 | 32 | 33 | ### Features 34 | 35 | * add type definitions ([#8](https://github.com/nuxt/lmify/issues/8)) ([84eb206](https://github.com/nuxt/lmify/commit/84eb206)) 36 | 37 | 38 | 39 | # 0.1.0 (2019-03-23) 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 - Pooya Parsa - Nuxt.js Team 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lmify 2 | 3 | > Let Me Install It For You! 4 | > Install NPM dependencies programmatically 🤙 5 | 6 | ## **This package is discontinued. Checkout [unjs/nypm](https://github.com/unjs/nypm) for a much better alternative!** 7 | 8 | ## Features 9 | 10 | - Support [Yarn](https://yarnpkg.com) and [NPM](https://docs.npmjs.com/cli/npm) 11 | - Auto package manager detection 12 | - Using Yarn if `yarn.lock` 13 | - Using NPM if `package-lock.json` or `package.json` 14 | - Singleton or Class API 15 | - Optional granters 16 | 17 | ## Install 18 | 19 | Using yarn: 20 | 21 | ```bash 22 | yarn add lmify 23 | ``` 24 | 25 | Using npm: 26 | 27 | ```bash 28 | npm install lmify 29 | ``` 30 | 31 | ## Usage 32 | 33 | ### `install(package|packages)` 34 | 35 | Install one or more packages in rootDir using the preferred package manager. 36 | 37 | ```js 38 | const { install } = require('lmify') 39 | 40 | await install('package-name') 41 | ``` 42 | 43 | ### `setPackageManager(name)` 44 | 45 | Set preferred package manager to use. By default, it will be guessed. 46 | 47 | ```js 48 | const { setPackageManager } = require('lmify') 49 | 50 | setPackageManager('yarn') 51 | ``` 52 | 53 | ### `setRootDir(rootDir)` 54 | 55 | Set project root dir. This causes package manager detection to happen on the next install. 56 | 57 | ```js 58 | const { setRootDir } = require('lmify') 59 | 60 | setRootDir(proccess.cwd()) 61 | ``` 62 | 63 | ### `addGranter(fn)` 64 | 65 | Add a granter function to ask the user before installing packages. 66 | 67 | This function accepts an array of packages to be added and should return `Promise`. 68 | 69 | Without a granter, install immediately adds package. 70 | 71 | If multiple granters added, the first response will be used (either deny or allow). 72 | 73 | ```js 74 | const { addGranter } = require('lmify') 75 | 76 | addGranter(async packages => { 77 | console.log('Installing packages:', packages) 78 | return true // Allow 79 | }) 80 | ``` 81 | 82 | ### Class: `LMIFY` 83 | 84 | You can choose between using singleton instance or creating a new instance of `LMIFY`: 85 | 86 | 87 | ```js 88 | const LMIFY = require('lmify') 89 | 90 | const constumInstance = new LMIFY(options) 91 | ``` 92 | 93 | #### `options` 94 | 95 | - `stdout`: Defaults to `process.stdout` 96 | - `stderr`: Defaults to `process.stderr` 97 | - `rootDir`: Defaults to `process.cwd()` 98 | - `packageManager`: Better if specified or `install` will throw a warning if no package manager is detected 99 | 100 | ## License 101 | 102 | MIT - Made with 💖 by Nuxt.js team! 103 | 104 | 105 | [npm-version-src]: https://flat.badgen.net/npm/dt/lmify 106 | [npm-version-href]: https://npmjs.com/package/lmify 107 | 108 | [npm-downloads-src]: https://flat.badgen.net/npm/v/lmify 109 | [npm-downloads-href]: https://npmjs.com/package/lmify 110 | 111 | [github-actions-ci-src]: https://github.com/nuxt/lmify/workflows/ci/badge.svg 112 | [github-actions-ci-href]: https://github.com/nuxt/lmify/actions?query=workflow%3Aci 113 | 114 | [codecov-src]: https://flat.badgen.net/codecov/c/github/nuxt/lmify 115 | [codecov-href]: https://codecov.io/gh/nuxt/lmify 116 | 117 | [david-dm-src]: https://flat.badgen.net/david/dep/nuxt/lmify 118 | [david-dm-href]: https://david-dm.org/nuxt/lmify 119 | 120 | [standard-js-src]: https://flat.badgen.net/badge/code%20style/standard/f2a 121 | [standard-js-href]: https://standardjs.com 122 | 123 | [packagephobia-src]: https://flat.badgen.net/packagephobia/install/lmify 124 | [packagephobia-href]: https://packagephobia.now.sh/result?p=lmify 125 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | collectCoverage: true, 4 | collectCoverageFrom: [ 5 | 'src/**/*.js' 6 | ], 7 | transform: { 8 | '^.+\\.js$': 'babel-jest' 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lmify", 3 | "version": "0.3.0", 4 | "description": "Programmatically install NPM dependencies to the project!", 5 | "repository": "nuxt/lmify", 6 | "typings": "types/lmify.d.ts", 7 | "main": "dist/lmify.js", 8 | "files": [ 9 | "dist", 10 | "types/*.d.ts" 11 | ], 12 | "scripts": { 13 | "build": "yarn clean && bili src/index.js --file-name lmify.js --format cjs", 14 | "clean": "rimraf dist test/fixtrues/*/node_modules", 15 | "lint": "yarn eslint .", 16 | "test": "yarn lint && yarn clean && yarn jest", 17 | "prepublish": "yarn build", 18 | "release": "standard-version && yarn build && git push --follow-tags && npm publish" 19 | }, 20 | "dependencies": { 21 | "execa": "^4.1.0", 22 | "fs-extra": "^9.1.0", 23 | "std-env": "^2.3.1" 24 | }, 25 | "devDependencies": { 26 | "@babel/preset-env": "^7.16.11", 27 | "@nuxtjs/eslint-config": "^2.0.2", 28 | "@types/execa": "^2.0.0", 29 | "babel-jest": "^25.5.1", 30 | "bili": "^4.10.1", 31 | "codecov": "^3.8.2", 32 | "eslint": "^7.32.0", 33 | "eslint-config-standard": "^14.1.1", 34 | "eslint-plugin-import": "^2.26.0", 35 | "eslint-plugin-jest": "^23.20.0", 36 | "eslint-plugin-node": "^11.1.0", 37 | "eslint-plugin-promise": "^4.3.1", 38 | "eslint-plugin-standard": "^4.1.0", 39 | "eslint-plugin-vue": "^6.2.2", 40 | "esm": "^3.2.25", 41 | "jest": "^25.5.4", 42 | "npm": "^6.14.16", 43 | "rimraf": "^3.0.2", 44 | "standard-version": "^7.1.0", 45 | "yarn": "^1.22.18" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "github>unjs/renovate-config" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { Workspace } from './workspace' 2 | import { PackageManager } from './package-manager' 3 | 4 | export default class LMIFY { 5 | constructor (options) { 6 | this.options = { 7 | stdout: process.stdout, 8 | stderr: process.stderr, 9 | rootDir: process.cwd(), 10 | packageManager: undefined, 11 | ...options 12 | } 13 | 14 | this._granters = [] 15 | } 16 | 17 | init () { 18 | if (!this._initPromise) { 19 | this._initPromise = this._init() 20 | } 21 | return this._initPromise 22 | } 23 | 24 | async _init () { 25 | this.workspace = new Workspace(this.options) 26 | this.packageManager = new PackageManager(this.options, this.workspace) 27 | await this.packageManager.detect() 28 | } 29 | 30 | setPackageManager (name) { 31 | this.options.packageManager = name 32 | } 33 | 34 | setRootDir (rootDir) { 35 | this.options.rootDir = rootDir 36 | delete this._initPromise 37 | } 38 | 39 | addGranter (granter) { 40 | this._granters.push(granter) 41 | } 42 | 43 | _grant (packages) { 44 | if (!this._granters.length) { 45 | return Promise.resolve(true) 46 | } 47 | return Promise 48 | .race(this._granters.map(granter => granter(packages))) 49 | .then(r => Boolean(r)) 50 | } 51 | 52 | async install (packages) { 53 | if (!packages) { 54 | return 55 | } 56 | 57 | if (!Array.isArray(packages)) { 58 | packages = [packages] 59 | } 60 | 61 | if (!packages.length) { 62 | return 63 | } 64 | 65 | const [grant] = await Promise.all([ 66 | this._grant(packages), 67 | this.init() 68 | ]) 69 | 70 | if (!grant) { 71 | return 72 | } 73 | 74 | return this.packageManager.install(packages) 75 | } 76 | } 77 | 78 | // Static methods 79 | const instance = new LMIFY() 80 | LMIFY.instance = instance 81 | for (const method of ['install', 'addGranter', 'setPackageManager', 'setRootDir']) { 82 | LMIFY[method] = LMIFY.instance[method].bind(LMIFY.instance) 83 | } 84 | -------------------------------------------------------------------------------- /src/package-manager.js: -------------------------------------------------------------------------------- 1 | import PackageManagers from './package-managers' 2 | 3 | export class PackageManager { 4 | constructor (options, workspace) { 5 | this.options = options 6 | this.workspace = workspace 7 | 8 | this.detectedPackageManager = null 9 | this.packageManagers = {} 10 | } 11 | 12 | async detect () { 13 | // Detect PM if no preferred one is set 14 | if (!this.options.packageManager) { 15 | for (const name in PackageManagers) { 16 | if (await PackageManagers[name].detect(this.workspace)) { 17 | this.detectedPackageManager = name 18 | break 19 | } 20 | } 21 | } 22 | 23 | // If nothing detected 24 | if (!this.options.packageManager && !this.detectedPackageManager) { 25 | this.detectedPackageManager = 'nop' 26 | // eslint-disable-next-line no-console 27 | console.warn( 28 | 'No package manager detected in ' + this.options.rootDir + '\n' + 29 | 'Ignoring any install command. Please use `lmify.setPackageManager(name)`') 30 | } 31 | 32 | // Validate it 33 | await this.getPackageManager() 34 | } 35 | 36 | getPackageManager () { 37 | const name = this.options.packageManager || this.detectedPackageManager 38 | 39 | if (!this.packageManagers[name]) { 40 | if (!PackageManagers[name]) { 41 | throw new Error('Unknown package manager: ' + name) 42 | } 43 | 44 | this.packageManagers[name] = new PackageManagers[name](this.options, this.workspace) 45 | } 46 | 47 | return this.packageManagers[name] 48 | } 49 | 50 | install (packages) { 51 | return this.getPackageManager().install(packages) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/package-managers/index.js: -------------------------------------------------------------------------------- 1 | import { NPM } from './npm' 2 | import { Yarn } from './yarn' 3 | import { Nop } from './nop' 4 | 5 | export default { 6 | yarn: Yarn, 7 | npm: NPM, 8 | nop: Nop 9 | } 10 | -------------------------------------------------------------------------------- /src/package-managers/nop.js: -------------------------------------------------------------------------------- 1 | export class Nop { 2 | static detect () { 3 | return Promise.resolve(false) 4 | } 5 | 6 | install () { 7 | return Promise.resolve() 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/package-managers/npm.js: -------------------------------------------------------------------------------- 1 | 2 | export class NPM { 3 | constructor (options, workspace) { 4 | this.workspace = workspace 5 | } 6 | 7 | static async detect (workspace) { 8 | if (await workspace.exists('package-lock.json')) { 9 | return true 10 | } 11 | if (await workspace.exists('package.json')) { 12 | return true 13 | } 14 | } 15 | 16 | install (packages) { 17 | return this.workspace.exec('npm', [ 18 | 'install', 19 | ...packages 20 | ]) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/package-managers/yarn.js: -------------------------------------------------------------------------------- 1 | export class Yarn { 2 | constructor (options, workspace) { 3 | this.workspace = workspace 4 | } 5 | 6 | static async detect (workspace) { 7 | if (await workspace.exists('yarn.lock')) { 8 | return true 9 | } 10 | } 11 | 12 | install (packages) { 13 | return this.workspace.exec('yarn', [ 14 | 'add', 15 | ...packages 16 | ]) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/workspace.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import fs from 'fs-extra' 3 | import execa from 'execa' 4 | 5 | export class Workspace { 6 | constructor (options) { 7 | this.options = options 8 | } 9 | 10 | path (filePath) { 11 | return path.join(this.options.rootDir, filePath) 12 | } 13 | 14 | exists (filePath) { 15 | return fs.exists(this.path(filePath)) 16 | } 17 | 18 | exec (cmd, args) { 19 | return execa(cmd, args, { 20 | stdout: this.options.stdout, 21 | stderr: this.options.stderr, 22 | cwd: this.options.rootDir 23 | }) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/detect.test.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import LMIFY from '../src/index' 3 | 4 | for (const name of ['npm', 'yarn']) { 5 | test('Detect ' + name, async () => { 6 | const rootDir = path.join(__dirname, 'fixtures', name) 7 | const lmify = new LMIFY({ rootDir }) 8 | await lmify.init() 9 | expect(lmify.packageManager.detectedPackageManager).toBe(name) 10 | }) 11 | } 12 | 13 | test('Detect yarn as priority if both yarn.lock and package-lock.json exists', async () => { 14 | const rootDir = path.join(__dirname, 'fixtures', 'yarn_and_npm') 15 | const lmify = new LMIFY({ rootDir }) 16 | await lmify.init() 17 | expect(lmify.packageManager.detectedPackageManager).toBe('yarn') 18 | }) 19 | 20 | test('Detect npm as fallback when no package-lock.json found', async () => { 21 | const rootDir = path.join(__dirname, 'fixtures', 'npm_fallback') 22 | const lmify = new LMIFY({ rootDir }) 23 | await lmify.init() 24 | expect(lmify.packageManager.detectedPackageManager).toBe('npm') 25 | }) 26 | 27 | test('No PackageManager', async () => { 28 | const rootDir = path.join(__dirname, 'fixtures', '404') 29 | 30 | const origin = global.console.warn 31 | global.console.warn = jest.fn() 32 | 33 | const lmify = new LMIFY({ rootDir }) 34 | await lmify.init() 35 | expect(global.console.warn).toHaveBeenCalled() 36 | 37 | global.console.warn = origin 38 | }) 39 | -------------------------------------------------------------------------------- /test/fixtures/npm/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "npm_fixture", 3 | "requires": true, 4 | "lockfileVersion": 1, 5 | "dependencies": { 6 | "ci-info": { 7 | "version": "1.6.0", 8 | "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", 9 | "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==" 10 | }, 11 | "define-properties": { 12 | "version": "1.1.3", 13 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", 14 | "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", 15 | "requires": { 16 | "object-keys": "^1.0.12" 17 | } 18 | }, 19 | "is-nan": { 20 | "version": "1.3.0", 21 | "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.0.tgz", 22 | "integrity": "sha512-z7bbREymOqt2CCaZVly8aC4ML3Xhfi0ekuOnjO2L8vKdl+CttdVoGZQhd4adMFAsxQ5VeRVwORs4tU8RH+HFtQ==", 23 | "requires": { 24 | "define-properties": "^1.1.3" 25 | } 26 | }, 27 | "object-keys": { 28 | "version": "1.1.1", 29 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 30 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" 31 | }, 32 | "std-env": { 33 | "version": "2.2.1", 34 | "resolved": "https://registry.npmjs.org/std-env/-/std-env-2.2.1.tgz", 35 | "integrity": "sha512-IjYQUinA3lg5re/YMlwlfhqNRTzMZMqE+pezevdcTaHceqx8ngEi1alX9nNCk9Sc81fy1fLDeQoaCzeiW1yBOQ==", 36 | "requires": { 37 | "ci-info": "^1.6.0" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/fixtures/npm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "npm_fixture", 3 | "private": true, 4 | "license": "MIT", 5 | "scripts": { 6 | "init": "npm i" 7 | }, 8 | "dependencies": { 9 | "is-nan": "^1.3.0", 10 | "std-env": "^2.2.1" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/fixtures/npm_fallback/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "npm_fallback_fixture", 3 | "private": true, 4 | "license": "MIT", 5 | "scripts": { 6 | "init": "npm i" 7 | }, 8 | "dependencies": { 9 | "is-nan": "^1.2.1", 10 | "std-env": "^2.2.1" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/fixtures/yarn/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yarn_fixture", 3 | "private": true, 4 | "license": "MIT", 5 | "scripts": { 6 | "init": "yarn" 7 | }, 8 | "dependencies": { 9 | "is-nan": "^1.3.0", 10 | "std-env": "^2.2.1" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/fixtures/yarn/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | ci-info@^1.6.0: 6 | version "1.6.0" 7 | resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" 8 | integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== 9 | 10 | define-properties@^1.1.3: 11 | version "1.1.3" 12 | resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" 13 | integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== 14 | dependencies: 15 | object-keys "^1.0.12" 16 | 17 | is-nan@^1.3.0: 18 | version "1.3.0" 19 | resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.0.tgz#85d1f5482f7051c2019f5673ccebdb06f3b0db03" 20 | integrity sha512-z7bbREymOqt2CCaZVly8aC4ML3Xhfi0ekuOnjO2L8vKdl+CttdVoGZQhd4adMFAsxQ5VeRVwORs4tU8RH+HFtQ== 21 | dependencies: 22 | define-properties "^1.1.3" 23 | 24 | object-keys@^1.0.12: 25 | version "1.1.0" 26 | resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.0.tgz#11bd22348dd2e096a045ab06f6c85bcc340fa032" 27 | integrity sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg== 28 | 29 | std-env@^2.2.1: 30 | version "2.2.1" 31 | resolved "https://registry.yarnpkg.com/std-env/-/std-env-2.2.1.tgz#2ffa0fdc9e2263e0004c1211966e960948a40f6b" 32 | integrity sha512-IjYQUinA3lg5re/YMlwlfhqNRTzMZMqE+pezevdcTaHceqx8ngEi1alX9nNCk9Sc81fy1fLDeQoaCzeiW1yBOQ== 33 | dependencies: 34 | ci-info "^1.6.0" 35 | -------------------------------------------------------------------------------- /test/fixtures/yarn_and_npm/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "npm_fixture", 3 | "requires": true, 4 | "lockfileVersion": 1, 5 | "dependencies": { 6 | "ci-info": { 7 | "version": "1.6.0", 8 | "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", 9 | "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==" 10 | }, 11 | "define-properties": { 12 | "version": "1.1.3", 13 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", 14 | "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", 15 | "requires": { 16 | "object-keys": "^1.0.12" 17 | } 18 | }, 19 | "is-nan": { 20 | "version": "1.3.0", 21 | "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.0.tgz", 22 | "integrity": "sha512-z7bbREymOqt2CCaZVly8aC4ML3Xhfi0ekuOnjO2L8vKdl+CttdVoGZQhd4adMFAsxQ5VeRVwORs4tU8RH+HFtQ==", 23 | "requires": { 24 | "define-properties": "^1.1.3" 25 | } 26 | }, 27 | "object-keys": { 28 | "version": "1.1.1", 29 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 30 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" 31 | }, 32 | "std-env": { 33 | "version": "2.2.1", 34 | "resolved": "https://registry.npmjs.org/std-env/-/std-env-2.2.1.tgz", 35 | "integrity": "sha512-IjYQUinA3lg5re/YMlwlfhqNRTzMZMqE+pezevdcTaHceqx8ngEi1alX9nNCk9Sc81fy1fLDeQoaCzeiW1yBOQ==", 36 | "requires": { 37 | "ci-info": "^1.6.0" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/fixtures/yarn_and_npm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yarn_and_npm_fixture", 3 | "private": true, 4 | "license": "MIT", 5 | "scripts": { 6 | "init": "yarn" 7 | }, 8 | "dependencies": { 9 | "is-nan": "^1.3.0", 10 | "std-env": "^2.2.1" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/fixtures/yarn_and_npm/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | ci-info@^1.6.0: 6 | version "1.6.0" 7 | resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" 8 | integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== 9 | 10 | define-properties@^1.1.3: 11 | version "1.1.3" 12 | resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" 13 | integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== 14 | dependencies: 15 | object-keys "^1.0.12" 16 | 17 | is-nan@^1.3.0: 18 | version "1.3.0" 19 | resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.0.tgz#85d1f5482f7051c2019f5673ccebdb06f3b0db03" 20 | integrity sha512-z7bbREymOqt2CCaZVly8aC4ML3Xhfi0ekuOnjO2L8vKdl+CttdVoGZQhd4adMFAsxQ5VeRVwORs4tU8RH+HFtQ== 21 | dependencies: 22 | define-properties "^1.1.3" 23 | 24 | object-keys@^1.0.12: 25 | version "1.1.0" 26 | resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.0.tgz#11bd22348dd2e096a045ab06f6c85bcc340fa032" 27 | integrity sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg== 28 | 29 | std-env@^2.2.1: 30 | version "2.2.1" 31 | resolved "https://registry.yarnpkg.com/std-env/-/std-env-2.2.1.tgz#2ffa0fdc9e2263e0004c1211966e960948a40f6b" 32 | integrity sha512-IjYQUinA3lg5re/YMlwlfhqNRTzMZMqE+pezevdcTaHceqx8ngEi1alX9nNCk9Sc81fy1fLDeQoaCzeiW1yBOQ== 33 | dependencies: 34 | ci-info "^1.6.0" 35 | -------------------------------------------------------------------------------- /test/grant.test.js: -------------------------------------------------------------------------------- 1 | import LMIFY from '../src/index' 2 | 3 | test('Grant by default', async () => { 4 | const lmify = new LMIFY() 5 | expect(await lmify._grant()).toBe(true) 6 | }) 7 | 8 | test('Grant true', async () => { 9 | const lmify = new LMIFY() 10 | lmify.addGranter(() => Promise.resolve(true)) 11 | expect(await lmify._grant()).toBe(true) 12 | }) 13 | 14 | test('Grant false', async () => { 15 | const lmify = new LMIFY() 16 | 17 | lmify.addGranter(() => Promise.resolve(false)) 18 | expect(await lmify._grant()).toBe(false) 19 | 20 | await lmify.init() 21 | 22 | const install = lmify.packageManager.install = jest.fn() 23 | 24 | await lmify.install('foo') 25 | expect(install).not.toBeCalled() 26 | }) 27 | -------------------------------------------------------------------------------- /test/install.test.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import fs from 'fs-extra' 3 | import LMIFY from '../src/index' 4 | 5 | for (const name of ['npm', 'yarn']) { 6 | test('Install with ' + name, async () => { 7 | const rootDir = path.join(__dirname, 'fixtures', name) 8 | 9 | const lmify = new LMIFY({ 10 | rootDir, 11 | stdout: 'ignore', 12 | stderr: 'ignore' 13 | }) 14 | 15 | await lmify.install(['is-nan', 'std-env']) 16 | 17 | expect(await fs.exists(path.join(rootDir, 'node_modules', 'is-nan'))).toBe(true) 18 | expect(await fs.exists(path.join(rootDir, 'node_modules', 'std-env'))).toBe(true) 19 | }, 60000) 20 | } 21 | -------------------------------------------------------------------------------- /test/lmify.test.js: -------------------------------------------------------------------------------- 1 | import LMIFY from '../src/index' 2 | 3 | test('setRootDir', () => { 4 | LMIFY.setRootDir('404') 5 | expect(LMIFY.instance.options.rootDir).toBe('404') 6 | LMIFY.setRootDir(process.cwd()) 7 | }) 8 | 9 | test('setPackageManager', async () => { 10 | LMIFY.setPackageManager('foo') 11 | expect(LMIFY.instance.options.packageManager).toBe('foo') 12 | await expect(LMIFY.instance.init()).rejects.toThrow('Unknown package manager: foo') 13 | delete LMIFY.instance._initPromise 14 | }) 15 | 16 | test('install', async () => { 17 | LMIFY.setPackageManager('nop') 18 | await LMIFY.instance.init() 19 | jest.spyOn(LMIFY.instance.packageManager, 'install') 20 | 21 | await LMIFY.instance.install() 22 | await LMIFY.instance.install([]) 23 | expect(LMIFY.instance.packageManager.install).not.toHaveBeenCalled() 24 | 25 | await LMIFY.instance.install('boo') 26 | expect(LMIFY.instance.packageManager.install).toHaveBeenCalled() 27 | }) 28 | -------------------------------------------------------------------------------- /types/lmify.d.ts: -------------------------------------------------------------------------------- 1 | import * as execa from 'execa' 2 | 3 | declare class Lmify { 4 | 5 | // for initalize 6 | init(): void 7 | private _init(): Promise 8 | 9 | // options 10 | setPackageManager(name: 'npm' | 'yarn'): void 11 | setRootDir(): void 12 | 13 | // permissions 14 | addGranter(granter: () => Promise): void 15 | _grant(packages: string): Promise 16 | 17 | // commands 18 | install(packages: string[]): Promise | execa.ExecaReturns 19 | } 20 | 21 | declare var lmify: Lmify; 22 | export = lmify; 23 | --------------------------------------------------------------------------------