Skip to content

Commit

Permalink
feat: final v3 features (#779)
Browse files Browse the repository at this point in the history
* feat: add charAliases

* feat: add Flags.option

* fix: undo default options

* fix: allow bin/dev.js to auto-transpile ESM plugin

* chore: update execute examples

* fix: update tsnode skip logic

* chore: test debugging

* fix: ts-node skip logic

* fix: ts-node skip logic

* feat: cache relativePath and isESM in manifest

* fix: calculate id permutations at runtime to support backwards compatability

* perf: avoid findLegacyRoot for linked plugins

* chore: remove env var

* fix: improve perf metrics

* perf: improve perf debug output

* perf: more debug improvements

* test: compilation errors

* fix: make relativePath OS safe

* test: use sf esm branch

* perf: give full hook path

* chore: test debugging

* chore: test debugging

* chore: test debugging

* test: set shell for e2e tests

* fix: flag types respect defaults

* feat: node protocol

* test: move windows sf integration tests into separate job

* test: update assertion on plugins install test

* test: use correct file path

* test: use -Force

* test: use bash shell

* test: clean up

* test: remove shell option

* test: oclif/config, core v1, and core v2 interop tests

* chore: drop node 16

* test: remove lts/-1

* chore: code review

* chore: add pre core migration guide

* refactor: break up ModuleLoader

* chore: remove unicorn/consistent-function-scoping

* chore: remove unicorn/no-missing-imports

* chore: remove @typescript-eslint/no-empty-function

* chore: remove ban-ts-comment and ban-ts-ignore

* chore: add sort-import rule

* fix: throw error if non-multiple flag provided more than once

* test: mutliples of non-multiple flag test

* test: ut for charAliases (#791)

* test: duplicate aliases tests

* test: extend timeout

* test: split windows esm-cjs tests

* test: parallelize linux interop tests too

* test: typo

* test: use right executor

* test: use right executor for clean up

* chore: update eslint libs (#792)

* chore: update eslint libs

* chore: clean up

* chore: tests and linting

* test: windows paths

* fix: exports

* test: update run import

* chore: replaceAll

* fix: error exit codes

* fix: use es2021

* feat: use ES2022

* fix: use ES2021 again

* test: incorporate flags and args in esm-cjs tests

* test: use correct expected values

---------

Co-authored-by: Shane McLaughlin <shane.mclaughlin@salesforce.com>
  • Loading branch information
mdonnalley and mshanemc authored Sep 22, 2023
1 parent c1423d5 commit bd2461b
Show file tree
Hide file tree
Showing 104 changed files with 4,244 additions and 2,791 deletions.
19 changes: 0 additions & 19 deletions .eslintrc

This file was deleted.

14 changes: 14 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"extends": [
"oclif",
"oclif-typescript"
],
"rules": {
"sort-imports": "error",
"unicorn/prefer-module": "off",
"unicorn/import-style": "error",
"unicorn/no-array-reduce": "off",
"unicorn/prefer-array-some": "off",
"no-useless-constructor": "off"
}
}
1 change: 1 addition & 0 deletions .github/workflows/create-github-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ jobs:
# However, if this is a manual release (workflow_dispatch), then we want to disable skip-on-empty
# This helps recover from forgetting to add semantic commits ('fix:', 'feat:', etc.)
skip-on-empty: ${{ github.event_name == 'push' }}
generate-readme: false
# docs:
# # Most repos won't use this
# # Depends on the 'release' job to avoid git collisions, not for any functionality reason
Expand Down
64 changes: 50 additions & 14 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,11 @@ jobs:
needs: linux-unit-tests
strategy:
matrix:
os: ["ubuntu-latest", "windows-latest"]
node_version: [lts/-1, lts/*, latest]
os: [ubuntu-latest, windows-latest]
node_version: [lts/*, latest]
exclude:
- os: windows-latest
node_version: lts/*
- os: windows-latest
node_version: lts/-1
fail-fast: false
runs-on: ${{ matrix.os }}
steps:
Expand All @@ -32,39 +30,77 @@ jobs:
with:
node-version: ${{ matrix.node_version }}
cache: yarn
- run: yarn install --network-timeout 600000
- uses: salesforcecli/github-workflows/.github/actions/yarnInstallWithRetries@main
- run: yarn build
- run: yarn test:e2e
- if: runner.os == 'Windows'
run: yarn mocha --forbid-only "test/**/*.e2e.ts" --exclude "test/integration/sf.e2e.ts" --parallel --timeout 1200000
- if: runner.os == 'Linux'
run: yarn test:e2e
windows-sf-e2e:
# For whatever reason the windows-latest runner doesn't like it when you shell yarn commands in the sf repo
# which is an integral part of the setup for the tests. Instead, we replicate the setup here.
needs: linux-unit-tests
strategy:
fail-fast: false
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: latest
cache: yarn
- uses: salesforcecli/github-workflows/.github/actions/yarnInstallWithRetries@main
- run: yarn build
- run: yarn link
- run: New-Item -Path D:\a -Name "e2e" -ItemType "directory"
- run: New-Item -Path D:\a\e2e -Name "sf.e2e.ts" -ItemType "directory"
- run: |
git clone https://github.com/salesforcecli/cli.git --branch mdonnalley/esm
cd cli
$Json = Get-Content package.json | ConvertFrom-Json
$Json.dependencies | Add-Member -Force -MemberType NoteProperty -Name "@oclif/core" -Value "file:D:\a\core\core"
$Json.resolutions | Add-Member -MemberType NoteProperty -Name "@oclif/core" -Value "D:\a\core\core"
$Json | ConvertTo-Json -Depth 9 | Out-File package.json
yarn install --network-timeout 600000
yarn link @oclif/core
yarn build
working-directory: D:\a\e2e\sf.e2e.ts
- run: yarn mocha --forbid-only "test/integration/sf.e2e.ts" --parallel --timeout 1200000
env:
OCLIF_CORE_E2E_SKIP_SETUP: true
OCLIF_CORE_E2E_TEST_DIR: D:\a\e2e
DEBUG: e2e:*
esm-cjs-interop:
needs: linux-unit-tests
strategy:
matrix:
os: ["ubuntu-latest", "windows-latest"]
node_version: [lts/-1, lts/*, latest]
os: [ubuntu-latest, windows-latest]
node_version: [lts/*, latest]
test: [esm, cjs, precore, coreV1, coreV2]
exclude:
- os: windows-latest
node_version: lts/*
- os: windows-latest
node_version: lts/-1
fail-fast: false
runs-on: ${{ matrix.os }}
timeout-minutes: 60
timeout-minutes: 75
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node_version }}
cache: yarn
- run: yarn install --network-timeout 600000
- uses: salesforcecli/github-workflows/.github/actions/yarnInstallWithRetries@main
- run: yarn build
- run: yarn test:esm-cjs
- run: yarn test:esm-cjs --test=${{ matrix.test }}
nuts:
needs: linux-unit-tests
uses: salesforcecli/github-workflows/.github/workflows/externalNut.yml@main
strategy:
fail-fast: false
matrix:
os: ["ubuntu-latest", "windows-latest"]
os: [ubuntu-latest, windows-latest]
externalProjectGitUrl:
- https://github.com/salesforcecli/plugin-auth
- https://github.com/salesforcecli/plugin-data
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ base library for oclif CLIs
Migrating
=====

See the [v3 migration guide](./guides/V3_MIGRATION.md) for an overview of breaking changes that occurred between v2 and v3.

See the [v2 migration guide](./guides/V2_MIGRATION.md) for an overview of breaking changes that occurred between v1 and v2.

See the [v3 migration guide](./guides/V3_MIGRATION.md) for an overview of breaking changes that occurred between v2 and v3.
Migrating from `@oclif/config` and `@oclif/command`? See the [v1 migration guide](./guides/PRE_CORE_MIGRATION.md).

CLI UX
=====
Expand Down
182 changes: 182 additions & 0 deletions guides/PRE_CORE_MIGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
Migrating to @oclif/core from deprecated libraries
==============

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

- [Migrating to @oclif/core from deprecated libraries](#migrating-to-oclifcore-from-deprecated-libraries)
- [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 `ux`](#replace-cli-ux-library-with-ux)
- [Single command CLIs](#single-command-clis)

## Update Imports

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

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

With this import:

```typescript
import {Command, Flags, Topic, Help} from '@oclif/core';
```

## Update your bin scripts

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

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

We recommend that all oclif plugins specify the `main` field in their package.json so that we can begin working on supporting Yarn v2.

```json
{
"main": "lib/index.js"
}
```

All plugins will be required to have this field in the next major version of `@oclif/core`.

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

`@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`.

`@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.

Simply add the `additionalHelpFlags` and `additionalVersionFlags` properties to the oclif section of your package.json:

```json
{
"oclif": {
"additionalHelpFlags": ["-h"],
"additionalVersionFlags": ["-v"]
}
}
```

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

```json
{
"dependencies": {
"@oclif/plugin-version": "^3"
},
"oclif": {
"plugins": [
"@oclif/plugin-version"
]
}
}
```

## Configure the `topicSeparator`

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.

For colons:
```json
{
"oclif": {
"topicSeparator": ":"
}
}
```

For spaces:
```json
{
"oclif": {
"topicSeparator": " "
}
}
```

**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.

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

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`:

`const { args, flags } = this.parse(MyCommand)` => `const { args, flags } = await this.parse(MyCommand)`

## Update `default` property on flag definitions

The `default` property on flag definitions is now asynchronous. So you'll now need to await those.

Example:

```typescript
import {Command, Flags} from '@oclif/core'
import {readFile} from 'fs/promises'

function getTeam(): Promise<string> {
return readFile('team.txt', 'utf-8')
}

export const team = Flags.build({
char: 't',
description: 'team to use',
default: () => getTeam(),
})

export class MyCLI extends Command {
static flags = {
team: team(),
}

async run() {
const {flags} = this.parse(MyCLI)
if (flags.team) console.log(`--team is ${flags.team}`)
}
}
```

## Replace cli-ux library with `ux`

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 `ux` namespace, with the exception of the `cli` export which was identical to the `ux` export.

Old:

```typescript
import { cli } from 'cli-ux`

cli.log('hello world')
cli.action.start('doing things')
cli.action.stop()
```

New:

```typescript
import { ux } from '@oclif/core`

ux.log('hello world')
ux.action.start('doing things')
ux.action.stop()
```

## Single command CLIs

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`:

```json
"oclif": {
"default": ".",
"commands": "./lib"
}
```

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.
35 changes: 35 additions & 0 deletions guides/V3_MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Migrating to @oclif/core@V3
- [`noCacheDefault` flag property replaces `isWritingManifest`](#nocachedefault-flag-property-replaces-iswritingmanifest)
- [Features 🎉](#features-)
- [Cache Flexible taxonomy Command Permutations](#cache-flexible-taxonomy-command-permutations)
- [charAliases Flag Property](#charaliases-flag-property)
- [Flags.option](#flagsoption)


## BREAKING CHANGES ❗
Expand Down Expand Up @@ -114,3 +116,36 @@ export const mySensitiveFlag = Flags.string({
### Cache Flexible taxonomy Command Permutations

The command permutations for flexible taxonomy are now cached in the oclif.manifest.json allowing for quicker startup times.

### charAliases Flag Property

You can now define single character flag aliases using the `charAliases` property.

### Flags.option

There's a new flag type that infers the flag's type from the provided options.

In v2 you would have needed to do something like this,

```typescript
type Options = 'foo' | 'bar'
export default class MyCommand extends Command {
static flags = {
name: Flags.custom<Options>({
options: ['foo', 'bar'],
})(),
}
}
```

Now in v3 you can do this,

```typescript
export default class MyCommand extends Command {
static flags = {
name: Flags.option({
options: ['foo', 'bar'] as const,
})(),
}
}
```
Loading

0 comments on commit bd2461b

Please sign in to comment.