Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds package-manager tool #718

Merged
merged 6 commits into from
Apr 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion .circleci/createDirect.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,17 @@
* They are .gitignore'd by version control and quickly cleaned up after release.
*/

const directFiles = ['filesystem', 'strings', 'print', 'system', 'semver', 'http', 'patching', 'prompt']
const directFiles = [
'filesystem',
'strings',
'print',
'system',
'semver',
'http',
'patching',
'prompt',
'package-manager',
]

const fs = require('fs')

Expand Down
12 changes: 11 additions & 1 deletion .circleci/removeDirect.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,17 @@
* This file cleans up the build artifacts generated by createDirect.js.
*/

const directFiles = ['toolbox', 'filesystem', 'strings', 'print', 'system', 'semver', 'http', 'patching', 'prompt']
const directFiles = [
'filesystem',
'strings',
'print',
'system',
'semver',
'http',
'patching',
'prompt',
'package-manager',
]
const fs = require('fs')
directFiles.forEach(f => {
const filename = __dirname + '/../' + f + '.js'
Expand Down
5 changes: 3 additions & 2 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

Gluegun is a delightful toolkit for building Node-based command-line interfaces (CLIs) in TypeScript or modern JavaScript, with support for:

🌯 *parameters* - command line arguments and options<br />
🌯 *parameters* - command-line arguments and options<br />
🎛 *template* - generating files from templates<br />
🗄 *patching* - manipulating file contents<br />
💾 *filesystem* - moving files and directories around<br />
Expand All @@ -18,6 +18,7 @@ Gluegun is a delightful toolkit for building Node-based command-line interfaces
💃 *print* - printing pretty colors and tables<br />
👩‍✈️ *semver* - working with semantic versioning<br />
🎻 *strings* - manipulating strings & template data<br />
📦 *packageManager* - installing NPM packages with Yarn or NPM<br />

In addition, `gluegun` supports expanding your CLI's ecosystem with a robust set of easy-to-write plugins and extensions.

Expand Down Expand Up @@ -117,7 +118,7 @@ See the [runtime docs](./docs/runtime.md) for more details on building your own

# What's under the hood?

We've assembled an _all star cast_ of libraries to help you build your CLI.
We've assembled an _all-star cast_ of libraries to help you build your CLI.

⭐️ [ejs](https://github.com/mde/ejs) for templating<br />
⭐️ [semver](https://github.com/npm/node-semver) for version investigations<br />
Expand Down
30 changes: 16 additions & 14 deletions docs/toolbox-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,21 @@ module.exports = {

Here's what's available inside the `toolbox` object you see all over Gluegun.

| name | provides the... | 3rd party |
| -------------- | -------------------------------------------------- | ------------------------------ |
| **meta** | information about the currently running CLI | |
| **config** | configuration options from the app or plugin | |
| **filesystem** | ability to copy, move & delete files & directories | fs-jetpack |
| **http** | ability to talk to the web | apisauce |
| **parameters** | command line arguments and options | yargs-parser |
| **patching** | manipulating file contents easily | fs-jetpack |
| **print** | tools to print output to the command line | colors, ora |
| **prompt** | tools to acquire extra command line user input | enquirer |
| **semver** | utilities for working with semantic versioning | semver |
| **strings** | some string helpers like case conversion, etc. | lodash & ramda |
| **system** | ability to execute | node-which, execa, cross-spawn |
| **template** | code generation from templates | ejs |
| name | provides the... | 3rd party |
| ------------------ | -------------------------------------------------- | ------------------------------ |
| **meta** | information about the currently running CLI | |
| **config** | configuration options from the app or plugin | |
| **filesystem** | ability to copy, move & delete files & directories | fs-jetpack |
| **http** | ability to talk to the web | apisauce |
| **parameters** | command line arguments and options | yargs-parser |
| **patching** | manipulating file contents easily | fs-jetpack |
| **print** | tools to print output to the command line | colors, ora |
| **prompt** | tools to acquire extra command line user input | enquirer |
| **semver** | utilities for working with semantic versioning | semver |
| **strings** | some string helpers like case conversion, etc. | lodash & ramda |
| **system** | ability to execute | node-which, execa, cross-spawn |
| **template** | code generation from templates | ejs |
| **packageManager** | ability to add or remove packages with Yarn/NPM | |

The `toolbox` has "drawers" full of useful tools for building CLIs. For example, the `toolbox.meta.version` function can be invoked like this:

Expand Down Expand Up @@ -61,6 +62,7 @@ const { print, filesystem, strings } = require('gluegun')
const { print } = require('gluegun/print')
const { filesystem } = require('gluegun/filesystem')
const { strings } = require('gluegun/strings')
const { packageManager } = require('gluegun/package-manager')

print.info(`Hey, I'm Gluegun!`)
filesystem.dir('/tmp/jamon')
Expand Down
42 changes: 42 additions & 0 deletions docs/toolbox-package-manager.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
Provides an API for intelligently running commands in yarn or npm depending on which is installed.

## hasYarn

Whether the current system has yarn installed

```js
toolbox.packageManager.hasYarn() // true
```

## add (async)

Adds a package using yarn or npm

```js
await toolbox.packageManager.add('infinite_red', {
dev: true,
dryRun: false,
force: 'npm', //remove this to have the system determine which
})
```

Will return an object similar to the following:

```js
{
success: true,
command: 'npm install --save-dev infinite_red',
stdout: ''
}
```

## remove (async)

Removes a package using yarn or npm

```js
await toolbox.packageManager.remove('infinite_red', {
dryRun: false,
force: 'npm', //remove this to have the system determine which
})
```
1 change: 1 addition & 0 deletions package-manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./build/package-manager')
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
"temp-write": "4.0.0",
"ts-jest": "24.3.0",
"ts-node": "8.6.2",
"typescript": "3.7.5",
"typescript": "3.8.3",
"unique-temp-dir": "1.0.0"
},
"jest": {
Expand Down Expand Up @@ -161,7 +161,8 @@
],
"rules": {
"@typescript-eslint/explicit-function-return-type": 0,
"@typescript-eslint/no-explicit-any": 0
"@typescript-eslint/no-explicit-any": 0,
"@typescript-eslint/no-var-requires": 0
},
"overrides": [
{
Expand Down
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Gluegun is a delightful toolkit for building Node-based command-line interfaces
💃 *print* - printing pretty colors and tables<br />
👩‍✈️ *semver* - working with semantic versioning<br />
🎻 *strings* - manipulating strings & template data<br />
📦 *packageManager* - installing NPM packages with Yarn or NPM<br />

In addition, `gluegun` supports expanding your CLI's ecosystem with a robust set of easy-to-write plugins and extensions.

Expand Down
2 changes: 0 additions & 2 deletions src/cli/commands/new.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,10 @@ const NewCommand: GluegunCommand = {
// we default to TypeScript if they just press "enter"
const lang = (answer && answer.toLowerCase()) || 'typescript'

// eslint-disable-next-line require-atomic-updates
props.language = lang.includes('typescript') ? 'typescript' : 'javascript'
info(`Language used: ${props.language === 'typescript' ? 'TypeScript' : 'Modern JavaScript'}`)
}

// eslint-disable-next-line require-atomic-updates
props.extension = props.language === 'typescript' ? 'ts' : 'js'

// create the directory
Expand Down
2 changes: 1 addition & 1 deletion src/cli/templates/cli/src/cli.js.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ async function run(argv) {
.create()
// enable the following method if you'd like to skip loading one of these core extensions
// this can improve performance if they're not necessary for your project:
// .exclude(['meta', 'strings', 'print', 'filesystem', 'semver', 'system', 'prompt', 'http', 'template', 'patching'])
// .exclude(['meta', 'strings', 'print', 'filesystem', 'semver', 'system', 'prompt', 'http', 'template', 'patching', 'package-manager'])
// and run it
const toolbox = await cli.run(argv)

Expand Down
11 changes: 11 additions & 0 deletions src/core-extensions/package-manager-extension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { GluegunToolbox } from '../domain/toolbox'
import { packageManager } from '../toolbox/package-manager-tools'

/**
* Extensions to filesystem. Brought to you by fs-jetpack.
*
* @param toolbox The running toolbox.
*/
export default function attach(toolbox: GluegunToolbox) {
toolbox.packageManager = packageManager
}
2 changes: 1 addition & 1 deletion src/domain/builder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ test('the gauntlet', () => {

expect(runtime.brand).toBe('test')
expect(runtime.commands.length).toBe(34)
expect(runtime.extensions.length).toBe(15)
expect(runtime.extensions.length).toBe(16)
expect(runtime.defaultPlugin.commands.length).toBe(6)

const { commands } = runtime.defaultPlugin
Expand Down
3 changes: 3 additions & 0 deletions src/domain/toolbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
GluegunPrompt,
GluegunTemplate,
GluegunMeta,
GluegunPackageManager,
} from '..'

export interface GluegunParameters {
Expand Down Expand Up @@ -70,6 +71,7 @@ export interface GluegunToolbox extends GluegunEmptyToolbox {
system: GluegunSystem
template: GluegunTemplate
generate: any
packageManager: GluegunPackageManager
}

export class EmptyToolbox implements GluegunEmptyToolbox {
Expand Down Expand Up @@ -113,6 +115,7 @@ export class Toolbox extends EmptyToolbox implements GluegunToolbox {
system: GluegunSystem
template: GluegunTemplate
generate: any
packageManager: GluegunPackageManager
}

// Toolbox used to be known as RunContext. This is for backwards compatibility.
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export { semver, GluegunSemver } from './toolbox/semver-tools'
export { http, GluegunHttp } from './toolbox/http-tools'
export { patching, GluegunPatching, GluegunPatchingPatchOptions } from './toolbox/patching-tools'
export { prompt, GluegunPrompt } from './toolbox/prompt-tools'
export { packageManager, GluegunPackageManager } from './toolbox/package-manager-tools'

// TODO: can't export these tools directly as they require the toolbox to run
// need ideas on how to handle this
Expand Down
1 change: 1 addition & 0 deletions src/package-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { packageManager, GluegunPackageManager } from './toolbox/package-manager-tools'
8 changes: 5 additions & 3 deletions src/runtime/runtime-extensions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ test('loads the core extensions in the right order', () => {
r.addCoreExtensions()
const list = r.extensions.map(x => x.name).join(', ')

expect(list).toBe('meta, strings, print, filesystem, semver, system, prompt, http, template, patching')
expect(list).toBe(
'meta, strings, print, filesystem, semver, system, prompt, http, template, patching, package-manager',
)
})

test('loads async extensions correctly', async () => {
Expand All @@ -15,6 +17,6 @@ test('loads async extensions correctly', async () => {
r.addPlugin(`${__dirname}/../fixtures/good-plugins/threepack`)

const toolbox = await r.run('three')
expect(toolbox['asyncData']).toBeDefined()
expect(toolbox['asyncData'].a).toEqual(1)
expect(toolbox.asyncData).toBeDefined()
expect(toolbox.asyncData.a).toEqual(1)
})
3 changes: 2 additions & 1 deletion src/runtime/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class Runtime {
public defaultPlugin?: Plugin = null
public defaultCommand?: Command = null
public config: Options = {}
public checkUpdate: boolean = false
public checkUpdate = false
public run: (rawCommand?: string | Options, extraOptions?: Options) => Promise<GluegunToolbox>

/**
Expand Down Expand Up @@ -61,6 +61,7 @@ export class Runtime {
'http',
'template',
'patching',
'package-manager',
]

coreExtensions.forEach(ex => {
Expand Down
51 changes: 51 additions & 0 deletions src/toolbox/package-manager-tools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {
GluegunPackageManager,
GluegunPackageManagerOptions,
GluegunPackageManagerResult,
} from './package-manager-types'
import { system } from './system-tools'

let yarnpath

const hasYarn = () => {
if (yarnpath === undefined) {
yarnpath = system.which('yarn')
}
return Boolean(yarnpath)
}

const add = async (
packageName: string,
options: GluegunPackageManagerOptions,
): Promise<GluegunPackageManagerResult> => {
const yarn = options.force === undefined ? hasYarn() : options.force === 'yarn'
let dev = ''
if (options.dev === true) dev = yarn ? '--dev ' : '--save-dev '

const command = yarn ? `yarn add ${dev}${packageName}` : `npm install ${dev}${packageName}`
let stdout
if (!options.dryRun) {
stdout = await system.run(command)
}
return { success: true, command, stdout }
}

const remove = async (
packageName: string,
options: GluegunPackageManagerOptions,
): Promise<GluegunPackageManagerResult> => {
const command = hasYarn() ? `yarn remove ${packageName}` : `npm uninstall ${packageName}`
let stdout
if (!options.dryRun) {
stdout = await system.run(command)
}
return { success: true, command, stdout }
}

const packageManager: GluegunPackageManager = {
add,
remove,
hasYarn,
}

export { packageManager, GluegunPackageManager }
16 changes: 16 additions & 0 deletions src/toolbox/package-manager-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export type GluegunPackageManagerOptions = {
dev?: boolean
dryRun?: boolean
force?: 'npm' | 'yarn'
}
export type GluegunPackageManagerResult = {
success: boolean
command: string
stdout: string
error?: string
}
export type GluegunPackageManager = {
add: (packageName: string, options: GluegunPackageManagerOptions) => Promise<GluegunPackageManagerResult>
remove: (packageName: string, options: GluegunPackageManagerOptions) => Promise<GluegunPackageManagerResult>
hasYarn: () => boolean
}
Loading