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

Version 2.0.0 #539

Merged
merged 45 commits into from
Jan 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
de4b987
feat: improve interfaces
mdonnalley Oct 25, 2022
6af899b
feat: new command types
mdonnalley Oct 25, 2022
9c652a4
feat: more changes
mdonnalley Oct 25, 2022
81bf174
chore: clean up
mdonnalley Oct 25, 2022
fd00dcf
chore: enable eslint rules
mdonnalley Oct 25, 2022
5a90833
chore: add files
mdonnalley Oct 25, 2022
e3af076
feat: remove parser/deps
mdonnalley Oct 25, 2022
2aba777
chore: more clean up
mdonnalley Oct 25, 2022
e4cf57a
chore: tests
mdonnalley Oct 25, 2022
365ed50
feat: rename globalFlags to baseFlags
mdonnalley Oct 26, 2022
af83915
feat: typed args
mdonnalley Oct 26, 2022
18b56df
chore: renaming things and cleanup
mdonnalley Oct 26, 2022
7f47dc0
chore: make tests compilable
mdonnalley Oct 26, 2022
26451d3
chore: add args types test
mdonnalley Oct 26, 2022
731b2ed
chore: get tests passing
mdonnalley Oct 26, 2022
9482096
feat: flatten ux
mdonnalley Oct 26, 2022
e8e3489
Merge branch 'main' into mdonnalley/v2
mdonnalley Oct 26, 2022
fb09906
fix: ux.table export
mdonnalley Oct 26, 2022
8758cce
fix: read from stdin
mdonnalley Oct 27, 2022
1c0cf89
fix: args and cleanup
mdonnalley Oct 27, 2022
836410c
chore: tests
mdonnalley Oct 27, 2022
6c52574
fix: arg and flag types
mdonnalley Oct 27, 2022
42c4b35
fix: small bugs
mdonnalley Oct 28, 2022
f081707
fix: handle non-existent flags
mdonnalley Oct 28, 2022
7c41318
Merge branch 'main' into mdonnalley/v2
mdonnalley Oct 28, 2022
5671bff
chore: tests
mdonnalley Oct 28, 2022
bbf4440
Merge branch 'main' into mdonnalley/v2
mdonnalley Oct 31, 2022
a573e9c
chore: compilation issues
mdonnalley Oct 31, 2022
1174ff5
chore: update cli-ux docs
mdonnalley Oct 31, 2022
7643adc
feat: add execute
mdonnalley Nov 7, 2022
2735758
refactor: clean up cli-ux imports
mdonnalley Nov 8, 2022
2ab4ffd
Merge branch 'main' into mdonnalley/v2
mdonnalley Nov 8, 2022
99504b5
test: begin removing fancy-test
mdonnalley Nov 11, 2022
4139ebb
feat: remove ux.open
mdonnalley Jan 9, 2023
0f9c4ca
Merge branch 'main' into mdonnalley/v2
mdonnalley Jan 9, 2023
dbb6038
chore: cleam up from merge
mdonnalley Jan 10, 2023
5edfc4a
feat: add delimiter option
mdonnalley Jan 10, 2023
98c76f7
fix: deprecateAliases
mdonnalley Jan 10, 2023
0b7adc6
chore: update tests
mdonnalley Jan 10, 2023
152d39a
chore: update migration guide
mdonnalley Jan 11, 2023
727494e
fix: add backwards compatability for v1 args
mdonnalley Jan 11, 2023
21667f7
fix: typing for default context
mdonnalley Jan 11, 2023
a796e05
fix: styledObject type and boolean flag parser
mdonnalley Jan 11, 2023
3a23fb2
fix: types
mdonnalley Jan 11, 2023
9b2edcb
Merge branch 'prerelease/v2' into mdonnalley/v2
mdonnalley Jan 12, 2023
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
4 changes: 1 addition & 3 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
"oclif-typescript"
],
"rules": {
"unicorn/no-abusive-eslint-disable": "off",
"unicorn/prefer-spread": "off",
"unicorn/prefer-module": "off",
"unicorn/prefer-node-protocol": "off",
"unicorn/import-style": "off",
Expand All @@ -16,6 +14,6 @@
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/explicit-module-boundary-types": "off"
"no-useless-constructor": "off"
}
}
274 changes: 150 additions & 124 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -1,181 +1,207 @@
Migrating to @oclif/core
Migrating to @oclif/core@V2
==============

Migrating to `@oclif/core` from the old oclif libraries (`@oclif/config`, `@oclif/command`, `@oclif/error`, `@oclif/parser`) is relatively straight forward.
## Breaking Changes

- [Migrating to @oclif/core](#migrating-to-oclifcore)
- [Update Imports](#update-imports)
- [Update your bin scripts](#update-your-bin-scripts)
- [Add `main` to your package.json](#add-main-to-your-packagejson)
- [Restore `-h`, `-v`, and `version`](#restore--h--v-and-version)
- [Configure the `topicSeparator`](#configure-the-topicseparator)
- [Update `this.parse` to `await this.parse`](#update-thisparse-to-await-thisparse)
- [Update `default` property on flag definitions](#update-default-property-on-flag-definitions)
- [Replace cli-ux library with `CliUx`](#replace-cli-ux-library-with-cliux)
### Command Args

## Update Imports
We updated the `Command.args` to more closely resemble flags

Replace imports from the old libraries with `@oclif/core`. For example,
**Before**

```typescript
import Help from '@oclif/plugin-help';
import {Topic} from '@oclif/config';
import {Command, Flags} from '@oclif/command'
```
import { Command } from '@oclif/core'

With this import:
export default MyCommand extends Command {
static args = [{name: arg1, description: 'an argument', required: true}]

```typescript
import {Command, Flags, Topic, Help} from '@oclif/core';
public async run(): Promise<void> {
const {args} = await this.parse(MyCommand) // args is useless {[name: string]: any}
}
}
```

## Update your bin scripts

`@oclif/core` now supports separate bin scripts for production and development.
**After**

You can copy these new bin scripts directly from our [example repository](https://github.com/oclif/hello-world/tree/main/bin).

## Add `main` to your package.json
```typescript
import { Command, Args } from '@oclif/core'

We recommend that all oclif plugins specify the `main` field in their package.json so that we can begin working on supporting Yarn v2.
export default MyCommand extends Command {
static args = {
arg1: Args.string({description: 'an argument', required: true})
}

```json
{
"main": "lib/index.js"
public async run(): Promise<void> {
const {args} = await this.parse(MyCommand) // args is { arg1: string }
}
}
```

All plugins will be required to have this field in the next major version of `@oclif/core`.
These are the available Args:
- string
- integer
- boolean
- url
- file
- directory
- custom

## Restore `-h`, `-v`, and `version`
### Interfaces

`@oclif/config` automatically added `-h` as a short flag for `--help`, `-v` as a short flag for `--version`, and `version` as an alias for `--version`.
- Removed `Interfaces.Command` since they were not usable for tests. These are replaced by types that are available under the `Command` namespace

`@oclif/core` removes these so you can now use those flags for whatever you want! However, we've added a way to restore that functionality if you want to keep it.
```
Interfaces.Command => Command.Cached
Interfaces.Command.Class => Command.Class
Interfaces.Command.Loadable => Command.Lodable
```

Simply add the `additionalHelpFlags` and `additionalVersionFlags` properties to the oclif section of your package.json:
- Removed the following interfaces from the export. Exporting all of these made it difficult to make non-breaking changes when modifying types and/or fixing compilation bugs. We are open to PRs to reintroduce these to the export if they are needed for your project
- Arg
- ArgInput
- ArgToken
- CLIParseErrorOptions
- CompletableFlag
- CompletableOptionFlag
- Completion
- CompletionContext
- Default
- DefaultContext
- Definition
- EnumFlagOptions
- FlagBase
- FlagInput
- FlagOutput
- FlagToken
- FlagUsageOptions
- Input
- List
- ListItem
- Metadata
- OptionalArg
- OptionFlagProps
- OutputArgs
- OutputFlags
- ParseFn
- ParserArg
- ParserInput
- ParserOutput
- ParsingToken
- RequiredArg

### CliUx

We flattened `CliUx.ux` into `ux` for ease of use

**Before**

```json
{
"oclif": {
"additionalHelpFlags": ["-h"],
"additionalVersionFlags": ["-v"]
}
}
```
```typescript
import {CliUx} from '@oclif/core'

To get the `version` command, install `@oclif/plugin-version` into your CLI:

```json
{
"dependencies": {
"@oclif/plugin-version": "^1"
},
"oclif": {
"plugins": [
"@oclif/plugin-version"
]
}
}
CliUx.ux.log('Hello World')
```

## Configure the `topicSeparator`
**After**

By default, the `topicSeparator` is set to a colon (`:`) to maintain backwards compatibility with existing CLIs. If you prefer, you can now set it to a space.
```typescript
import {ux} from '@oclif/core'

For colons:
```json
{
"oclif": {
"topicSeparator": ":"
}
}
ux.log('Hello World')
```

For spaces:
```json
{
"oclif": {
"topicSeparator": " "
}
}
```
#### CliUx.ux.open

**NOTE: Using colons always works, even if you set the `topicSeparator` to spaces.** This means that you can enable spaces in your CLI without introducing a breaking change to your users.
We removed the `open` method since it was a direct import/export of the [`open`](https://www.npmjs.com/package/open) package. If you need this functionality, then you should import `open` yourself.

## Update `this.parse` to `await this.parse`
### Flags

The `parse` method on `Command` is now asynchronous (more [here](https://oclif.io/blog/#async-command-parsing)). So you'll now need to `await` any calls to `this.parse`:
- Flags.custom replaces Flags.build, Flags.enum, and Flags.option
- Removed builtin `color` flag
- Renamed `globalFlags` to `baseFlags`
- `globalFlags` was a misleading name because the flags added there weren't actually global to the entire CLI. Instead, they were just flags that would be inherited by any command that extended the command class they were defined in.

`const { args, flags } = this.parse(MyCommand)` => `const { args, flags } = await this.parse(MyCommand)`
### Flag and Arg Parsing

## Update `default` property on flag definitions
- In v1, any input that didn't match a flag definition was assumed to be an argument. This meant that misspelled flags, e.g. `--hekp` were parsed as arguments, instead of throwing an error. In order to handle this, oclif now assumes that anything that starts with a hyphen must be a flag and will throw an error if no corresponding flag definition is found. **In other words, your command can no longer accept arguments that begin with a hyphen** (fixes https://github.com/oclif/core/issues/526)
- v1 allowed you to return an array from a flag's `parse`. This was added to support backwards compatibility for flags that separated values by commas (e.g. `my-flag=val1,val2`). However, this was problematic because it didn't allow the `parse` to manipulate the individual values. If you need this functionality, you can now set a `delimiter` option on your flags. By doing so, oclif will split the string on the delimiter before parsing.

The `default` property on flag definitions is now asynchronous. So you'll now need to await those.
## ESM/CJS Friendliness

Example:
Writing plugins with ESM has always been possible, but it requires [a handful of modifications](https://oclif.io/docs/esm) for it to work, especially in the bin scripts. In v2 we've introduced an `execute` method that the bin scripts can use to avoid having to make changes for ESM of CJS.

**CJS `bin/dev` before**
```typescript
import {Command, Flags} from '@oclif/core'
import {readFile} from 'fs/promises'
#!/usr/bin/env node

function getTeam(): Promise<string> {
return readFile('team.txt', 'utf-8')
}
const oclif = require('@oclif/core')

export const team = Flags.build({
char: 't',
description: 'team to use',
default: () => getTeam(),
})
const path = require('path')
const project = path.join(__dirname, '..', 'tsconfig.json')

export class MyCLI extends Command {
static flags = {
team: team(),
}
// In dev mode -> use ts-node and dev plugins
process.env.NODE_ENV = 'development'

async run() {
const {flags} = this.parse(MyCLI)
if (flags.team) console.log(`--team is ${flags.team}`)
}
}
```
require('ts-node').register({project})

## Replace cli-ux library with `CliUx`
// In dev mode, always show stack traces
oclif.settings.debug = true;

The [`cli-ux` library](https://github.com/oclif/cli-ux) has also been moved into `@oclif/core` in order to break a complex circular dependency between the two projects.

All the exports that were available from `cli-ux` are now available under the `CliUx` namespace, with the exception of the `cli` export which was identical to the `ux` export.
// Start the CLI
oclif.run().then(oclif.flush).catch(oclif.Errors.handle)
```

Old:
**CJS `bin/dev.js` after**
```typescript
#!/usr/bin/env node
// eslint-disable-next-line node/shebang
(async () => {
const oclif = await import('@oclif/core')
await oclif.execute({type: 'cjs', development: true, dir: __dirname})
})()
```

**ESM `bin/dev.js` before**
```typescript
import { cli } from 'cli-ux`
#!/usr/bin/env ts-node

cli.log('hello world')
cli.action.start('doing things')
cli.action.stop()
```
/* eslint-disable node/shebang */

New:
import oclif from '@oclif/core'
import path from 'node:path'
import url from 'node:url'
// eslint-disable-next-line node/no-unpublished-import
import {register} from 'ts-node'

```typescript
import { CliUx } from '@oclif/core`
// In dev mode -> use ts-node and dev plugins
process.env.NODE_ENV = 'development'

CliUx.ux.log('hello world')
CliUx.ux.action.start('doing things')
CliUx.ux.action.stop()
```
register({
project: path.join(path.dirname(url.fileURLToPath(import.meta.url)), '..', 'tsconfig.json'),
})

## Single command CLIs
// In dev mode, always show stack traces
oclif.settings.debug = true

Single command CLIs now are configured in a different way. To ensure your migrated CLI work as before, you have to add the following to your `oclif` configuration in the `package.json`:
// Start the CLI
oclif
.run(process.argv.slice(2), import.meta.url)
.then(oclif.flush)
.catch(oclif.Errors.handle)
```

```json
"oclif": {
"default": ".",
"commands": "./lib"
}
**ESM `bin/dev.js` after**
```typescript
#!/usr/bin/env node
// eslint-disable-next-line node/shebang
(async () => {
const oclif = await import('@oclif/core')
await oclif.execute({type: 'esm', dir: import.meta.url})
})()
```

Where `./lib` points to the folder in which your `tsconfig.json` is configured to output to (if you are using TypeScript), and your single command CLI entrypoint `index.(ts|js)` is located.
Note that ESM and CJS plugins still require different settings in the tsconfig.json - you will still need to make those modifications yourself.

## Other Changes
- Removed dependency on `@oclif/screen`
- Replaced `@oclif/linewrap` with `wordwrap`
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@
base library for oclif CLIs

[![Version](https://img.shields.io/npm/v/@oclif/core.svg)](https://npmjs.org/package/@oclif/core)
[![CircleCI](https://circleci.com/gh/oclif/core/tree/main.svg?style=svg)](https://circleci.com/gh/oclif/core/tree/main)
[![Downloads/week](https://img.shields.io/npm/dw/@oclif/core.svg)](https://npmjs.org/package/@oclif/core)
[![License](https://img.shields.io/npm/l/@oclif/core.svg)](https://github.com/oclif/core/blob/main/package.json)


Migrating
=====

If you're migrating from the old oclif libraries (`@oclif/config`, `@oclif/command`, `@oclif/error`, `@oclif/parser`), see the [migration guide](./MIGRATION.md).
See the [migration guide](./MIGRATION.md) for an overview of breaking changes that occurred between v1 and v2.

The `@oclif/core` module now also includes the `cli-ux` module. Merging `cli-ux` into `@oclif/core` resolves a circular dependency between the two modules.
See the [cli-ux README](./src/cli-ux/README.md) for instructions on how to replace the `cli-ux` module with `@oclif/core`.
The [cli-ux README](./src/cli-ux/README.md) also contains detailed usage examples.
CLI UX
=====

The [ux README](./src/cli-ux/README.md) contains detailed usage examples of using the `ux` export.

Usage
=====
Expand Down
Loading