Coder Social home page Coder Social logo

core's Introduction

oclif: Node.JS Open CLI Framework

Version Downloads/week License

๐Ÿ—’ Description

This is a framework for building CLIs in Node.js. This framework was built out of the Salesforce CLI but generalized to build any custom CLI. It's designed both for single-file CLIs with a few flag options (like cat or ls), or for very complex CLIs that have subcommands (like git or heroku).

See the docs for more information.

๐Ÿš€ Getting Started Tutorial

The Getting Started tutorial is a step-by-step guide to introduce you to oclif. If you have not developed anything in a command line before, this tutorial is a great place to get started.

โœจ Features

  • Flag/Argument parsing - No CLI framework would be complete without a flag parser. We've built a custom one from years of experimentation that we feel consistently handles user input flexible enough for the user to be able to use the CLI in ways they expect, but without compromising strictness guarantees to the developer.
  • Super Speed - The overhead for running an oclif CLI command is almost nothing. It requires very few dependencies (only 28 dependencies in a minimal setupโ€”including all transitive dependencies). Also, only the command to be executed will be required with node. So large CLIs with many commands will load equally as fast as a small one with a single command.
  • CLI Generator - Run a single command to scaffold out a fully functional CLI and get started quickly. See Getting Started Tutorial.
  • Testing Helpers - We've put a lot of work into making commands easier to test and mock out stdout/stderr. The generator will automatically create scaffolded tests.
  • Auto-documentation - By default you can pass --help to the CLI to get help such as flag options and argument information. This information is also automatically placed in the README whenever the npm package of the CLI is published. See the hello-world CLI example
  • Plugins - Using plugins, users of the CLI can extend it with new functionality, a CLI can be split into modular components, and functionality can be shared amongst multiple CLIs. See Building your own plugin.
  • Hooks - Use lifecycle hooks to run functionality any time a CLI starts, or on custom triggers. Use this whenever custom functionality needs to be shared between various components of the CLI.
  • TypeScript - Everything in the core of oclif is written in TypeScript and the generator will build fully configured TypeScript CLIs. If you use plugins support, the CLI will automatically use ts-node to run the plugins enabling you to use TypeScript with minimal-to-no boilerplate needed for any oclif CLI.
  • Auto-updating Installers - oclif can package your CLI into different installers that will not require the user to already have node installed on the machine. These can be made auto-updatable by using plugin-update.
  • Everything is Customizable - Pretty much anything can be swapped out and replaced inside oclif if neededโ€”including the arg/flag parser.
  • Autocomplete - Automatically include autocomplete for your CLI. This includes not only command names and flag names, but flag values as well. For example, it's possible to configure the Heroku CLI to have completions for Heroku app names:
$ heroku info --app=<tab><tab> # will complete with all the Heroku apps a user has in their account

๐Ÿ“Œ Requirements

Currently, Node 18+ is supported. We support the LTS versions of Node. You can add the node package to your CLI to ensure users are running a specific version of Node.

๐Ÿ“Œ Migrating

See the v3 migration guide for an overview of breaking changes that occurred between v2 and v3.

See the v2 migration guide for an overview of breaking changes that occurred between v1 and v2.

Migrating from @oclif/config and @oclif/command? See the v1 migration guide.

๐Ÿ“Œ Documentation

The official oclif website, oclif.io, contains all the documentation you need for developing a CLI with oclif.

If there's anything you'd like to see in the documentation, please submit an issue on the oclif.github.io repo.

๐Ÿš€ Standalone Usage

We strongly encourage you generate an oclif CLI using the oclif cli. The generator will generate an npm package with @oclif/core as a dependency.

You can, however, use @oclif/core in a standalone script like this:

#!/usr/bin/env -S node --loader ts-node/esm --no-warnings=ExperimentalWarning

import * as fs from 'fs'
import {Command, Flags, flush, handle} from '@oclif/core'

class LS extends Command {
  static description = 'List the files in a directory.'
  static flags = {
    version: Flags.version(),
    help: Flags.help(),
    dir: Flags.string({
      char: 'd',
      default: process.cwd(),
    }),
  }

  async run() {
    const {flags} = await this.parse(LS)
    const files = fs.readdirSync(flags.dir)
    for (const f of files) {
      this.log(f)
    }
  }
}

LS.run().then(
  async () => {
    await flush()
  },
  async (err) => {
    await handle(err)
  },
)

Then run it like this:

$ ts-node myscript.ts
...files in current dir...

๐Ÿš€ Contributing

See the contributing guide.

core's People

Contributors

amcaplan avatar amphro avatar benatshippabo avatar cristiand391 avatar dependabot[bot] avatar dsokal avatar eob avatar f3l1x avatar iowillhoit avatar jayree avatar liaoyinglong avatar mdonnalley avatar michaelgoberling avatar mon-jai avatar mshanemc avatar nigelzor avatar oclif-bot avatar peternhale avatar rafaelmotaalves avatar rasphilco avatar rodesp avatar rolyatmax avatar rrthomas avatar shetzel avatar svc-cli-bot avatar typhonrt avatar willieruemmele avatar xlmnxp avatar y-lakhdar avatar yantze avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

core's Issues

Allow custom timeouts

I'm using the oclif generator (it's great, thanks!), but some of my commands are timing out when using the cli-ux flush implementation:

await timeout(flush(), 10_000)

Any chance you'd be willing to take a PR with optional parameter?

ThankS!

Flag typing in v1.12.0 & v1.12.1

Describe the bug
Flags typed as any in versions v1.12.0 & v1.12.1.

To Reproduce
Steps to reproduce the behavior:

  1. Upgrade to v1.12.0 from v1.11.0
  2. Use this.parse(Command) to grab flags
  3. See that flags are not typed

Expected behavior
Flags are typed as with release v1.11.0 and before.

Screenshots
v1.11.0
Screen Shot 2022-07-22 at 8 17 11 AM
v1.12.0
Screen Shot 2022-07-22 at 8 17 29 AM

Environment (please complete the following information):

  • OS & version: MacOS Monterey
  • Shell/terminal & version: zsh

Additional context
Tested in Visual Studio Code

Feature request: allow command flag to work exclusively as environment variable

Do you want to request a feature or report a bug?

Feature

What is the current behavior?

Whenever a flag is declared in a command with an environment field, it is still available as a normal flag for the command. The flag metadata only informs whether it was set from default or not.

What is the expected behavior?

In order to follow best practices regarding secret management, command flags containing secrets should only allow being inputted as environment variables. An envOnly option should suffice. Alternatively, adding the origin of the value (default, command flag, environment variable, etc) in the flag metadata, so it can be verified during runtime, could also be an option.

The code from Table's documentation page is incorrect and results in error after migration to 2.x

The code from Table's documentation page (from here) results in error:

async run() {
    CliUx.ux.table(users, {...}, {
      printLine: this.log, // <-- this is incorrect
      ...flags, // parsed flags
    })
  }

The error is:

TypeError: this.jsonEnabled is not a function
    at Object.log [as printLine] (D:\projects\karamba\git\vcode\packages\cli-client\node_modules\@oclif\core\lib\command.js:80:19)
    at Table.outputTable (D:\projects\karamba\git\vcode\packages\cli-client\node_modules\@oclif\core\lib\cli-ux\styled\table.js:234:21)
    at Table.display (D:\projects\karamba\git\vcode\packages\cli-client\node_modules\@oclif\core\lib\cli-ux\styled\table.js:105:22)
    at Object.table (D:\projects\karamba\git\vcode\packages\cli-client\node_modules\@oclif\core\lib\cli-ux\styled\table.js:279:39)

The correct example code is:

async run() {
    CliUx.ux.table(users, {...}, {
      printLine: this.log.bind(this), // <--
      ...flags, // parsed flags
    })
  }

JSON output breaks if a custom base Command is defined.

Describe the bug

If a custom base Command is defined WITH a custom init method, then the --json flag triggers the entire context to be logged.

To Reproduce
Steps to reproduce the behavior:

Define and use a custom base class with a custom init method.

// Base
export abstract class BaseCommand extends Command {
  static _globalFlags = {
    config: Flags.string({
      description: 'Specify config file'
    })
  };

  static flags = {}; // NOTE: Required otherwise _globalFlags is ignored.

  async init (): Promise<void> {
    await super.init();
    const { flags } = await this.parse(BaseCommand); // THIS CAUSES THE PROBLEM

    // Do custom init here based on global flag.
    if (flags.config) {
      this.userConfig = yaml.load(String(fs.readFileSync(configFile)));
    }
  }
}

// Command
export default class Status extends BaseCommand {
  static enableJsonFlag = true;

  async run (): Promise<{ status: number }> {
    const status = 200;
    this.log('Status:', status);
    return { status: 200 };
  }
}

Expected behavior
Invoking status --json should output { status: 200 }

The actual output is:

โ–ถ ./bin/dev cli status --json
{
  "error": {
    "oclif": {
      "exit": 2
    },
    "parse": {
      "input": {
        "argv": [
          "--json"
        ],
        "context": {
          "argv": [
            "--json"
          ],

Environment (please complete the following information):

  • OS & version: MacOS 12.3.1
  • Shell/terminal & version: zsh 5.8

Additional context

  • Regardless of this error the --json global flag never shows up in the help command output.
  • Additional global flags do not show up unless an empty static flags property is set: example:

Can't use typescript commands with ESM modules

Hi there,

I'm trying to create an oclif CLI as an ESM package that uses typescript, but am having trouble with using commands with the .ts extension. The CLI works fine if my command files are .js files, but the minute I rename them to .ts I get the following error:

$ ./bin/dev.js 
(node:73944) [ERR_REQUIRE_ESM] Error Plugin: my-cli: Must use import to load ES Module: /cli/src/commands/create/index.ts
require() of ES modules is not supported.
require() of /cli/src/commands/create/index.ts from /cli/node_modules/@oclif/core/lib/module-loader.js is an ES module file as it is a .ts file whose nearest parent package.json contains "type": "module" which defines all .ts files in that package scope as ES modules.
Instead change the requiring code to use import(), or remove "type": "module" from /cli/package.json.

module: @oclif/[email protected]
task: toCached
plugin: my-cli
root: /cli
See more details with DEBUG=*
(Use `node --trace-warnings ...` to show where the warning was created)
The CLI

VERSION
  my-cli/2.0.0 darwin-x64 node-v16.15.0

USAGE
  $ cli [COMMAND]

TOPICS
  plugins  List installed plugins.

COMMANDS
  help     Display help for cli.
  plugins  List installed plugins.

I tried digging around in the module-loader doing things like adding the .ts extension to s_EXTENSIONS and the isPathModule() method, but only seem to be digging a deeper hole. Does anyone have an example of how this is expected to work?

Source

./bin/dev.js

#!/usr/bin/env node

import oclif from '@oclif/core';
import path from 'node:path';
import url from 'node:url';
import { register } from 'ts-node';

const project = path.join(path.dirname(url.fileURLToPath(import.meta.url)), '..', 'tsconfig.json');

// In dev mode -> use ts-node and dev plugins
process.env.NODE_ENV = 'development';

register({ project });

// In dev mode, always show stack traces
oclif.settings.debug = true;

// Start the CLI
oclif
  .run(void 0, import.meta.url)
  .then(oclif.flush)
  .catch(oclif.Errors.handle);

package.json

{
  "name": "my-cli",
  "version": "2.0.0",
  "description": "The CLI",
  "type": "module",
  "exports": {
    ".": "./dist/index.js"
  },
  "files": [
    "/bin",
    "/dist",
    "/npm-shrinkwrap.json",
    "/oclif.manifest.json"
  ],
  "bin": {
    "cli": "./bin/run"
  },
  "oclif": {
    "bin": "cli",
    "dirname": "cli",
    "commands": "./dist/commands",
    "plugins": [
      "@oclif/plugin-help",
      "@oclif/plugin-plugins"
    ],
    "topicSeparator": " "
  },
  "dependencies": {
    "@oclif/core": "^1.8.2",
    "@oclif/plugin-help": "^5.1.12",
    "@oclif/plugin-plugins": "^2.1.0",
    "ajv": "^8.11.0",
    "axios": "^0.27.2",
    "cli-table": "^0.3.11",
    "execa": "^6.1.0",
    "inquirer": "^8.2.4"
  },
  "devDependencies": {
    "@oclif/test": "^2.1.0",
    "@types/cli-table": "^0.3.0",
    "@types/inquirer": "^8.2.1",
    "prettier": "^2.6.2",
    "shx": "^0.3.4",
    "ts-node": "^10.7.0",
    "typescript": "^4.6.4"
  },
  "scripts": {
    "build": "shx rm -rf dist && tsc -b",
    "lint": "eslint . --ext .ts --config .eslintrc",
    "postpack": "shx rm -f oclif.manifest.json",
    "posttest": "npm run lint",
    "prepack": "npm run build && oclif manifest && oclif readme",
    "version": "oclif readme && git add README.md"
  }
}

./src/commands/create/index.ts

import { Command } from '@oclif/core';

export default class CreateCommand extends Command {
  static description = 'Create a new resource';

  async run() {}
}

How to wait i18next setup before loading commands?

I'm using i18next to provide CLI documentation in multiple languages but can't set it up properly.

  1. init hook runs after commands have been loaded (t result in those cases is undefined);
  2. bin/run runs after commands have been loaded, but oclif readme don't use it;

Is there any configuration that I can use to set up i18next properly?

Error: resolvePath function can't resolve command file Path (.ts) in development mode.

Hi, we have migrated our oclif CLI to the core version following the migration guide

Note: This error doesn't happen in the previous version

I'm having an error when executing any commands in development mode, in this way for example:

  "scripts": {
     "run:init": "NODE_ENV=development ./bin/run init"
  }

When I run it using yarn: yarn run:init

I'm getting the following error:

(node:16497) [MODULE_NOT_FOUND] ModuleLoadError Plugin: @app/cli: [MODULE_NOT_FOUND] require failed to load /source/repos/cli/src/commands/init
module: @oclif/[email protected]
task: toCached
plugin: @app/cli
root: /source/repos/cli

Reviewing the code I saw that the error occurs in this method: resolvePath

image

Debugging error details:

Input values:

  • config.root = /source/repos/cli
  • modulePath = lib/commands/init

line 137 filePath = require.resolve(modulePath) // it fails and enters to the catch

line 140 filePath = Config.tsPath(config.root, modulePath) // filePath = /source/repos/cli/src/commands/init (It's okay)

In the previous line, It got the path to the typescrtipt files, and since the file has no extension,
in the line 145, It's append the supported extensions in the s_EXTENSIONS array to try to found it.

image

But since the '.ts' extension is not supported it can't find the file.

It's expected to build the following file path:

/source/repos/cli/src/commands/init.ts

Is that a bug? Or maybe there is something that I'm missing or misconfigured?
I look forward to your comments, thank you very much in advance!

Deno?

Would be nice if there was a deno version of this!

flag level help can show exclusive/dependsOn

some flag properties (required, default, options) show up at the flag-level help.

It would be cool if dependsOn and exclusive did, too.

Right now, we rely descriptions (or errors) to tell the user what they should have done.

Error Plugin: oclif: could not find package.json with ... '@oclif/plugin-warn-if-update-available'

I am trying to migrate to @oclif/core for a CLI contained in a monorepo using yarn 3 as a package manager in order to try and use the oclif pack functionality to package the CLI as an installer. You can see how I modified the package.json for the CLI here - first question, do these changes look correct?

My issue is that when trying to run yarn oclif manifest inside the CLI directory, I get the following error:

(node:246644) Error Plugin: oclif: could not find package.json with {
  type: 'core',
  root: '/home/strophy/Code/dashevo/platform/.yarn/cache/oclif-npm-2.1.0-2512c27eb7-b8c1c52405.zip/node_modules/oclif',
  name: '@oclif/plugin-warn-if-update-available'
}
module: @oclif/[email protected]
task: loadPlugins
plugin: oclif
root: /home/strophy/Code/dashevo/platform/.yarn/cache/oclif-npm-2.1.0-2512c27eb7-b8c1c52405.zip/node_modules/oclif
See more details with DEBUG=*
(Use `node --trace-warnings ...` to show where the warning was created)

This dependency comes from "oclif": "^2.1.0" and doesn't seem to be properly resolved by yarn. I tried adding the following to .yarnrc.yml like this:

  "@oclif/core@*":
    dependencies:
      "@oclif/plugin-warn-if-update-available": ^2.0.2

and/or

  "oclif@*":
    dependencies:
      "@oclif/plugin-warn-if-update-available": ^2.0.2

But the error remains. Can anyone help identify what is going wrong here?

How do I access command help inside command itself?

I want to print the command level help when the command is run, I used to do something like this in the old version

import { flags} from '@oclif/command';
import Command from '../base';

export class Config extends Command {
  static description = 'access configs'
  static flags = {
    help: flags.help({char: 'h'})
  }

  async run() {
    await this._help();
  }
}

In the new version, I don't think there is this._help() function.

How can I get the same functionality in the new version?

Core Overrides Linked Plugin's Command

Given:

  • mycli defines command foo
  • mycli-plugin defines commands foo and bar
  • the latter is installed into the former by cd into mycli dir and running yarn link then cd into mycli-plugin dir and run mycli plugins:link.

According to the documentation here, both foo and bar commands should come from mycli-plugin. However, only bar comes from mycli-plugin and mycli foo continues to execute the foo.js file in mycli and mycli which foo shows the mycli/core as the source for foo and mycli-plugin as the source for bar. Renaming mycli-plugin's foo.js to foo2.js immediately results in mycli having a new command, i.e., foo, foo2, and bar are all shown in --help and work as expected.

Presumably the override behavior has been changed or more likely there's a just a setup step to enable it that I've missed?

TS2305: Module '"@oclif/core"' has no exported member 'CliUx'.

Describe the bug
Following the directions as best as I can but receive the following when trying to implement the CliUx package

TS2305: Module '"@oclif/core"' has no exported member 'CliUx'.

To Reproduce
Steps to reproduce the behavior:

  1. Import from core library as show2.
import {Command, CliUx} from '@oclif/core'

export default class Do extends Command {
  static description = 'Do thing'

  async run(): Promise<void> {
    CliUx.ux.action.start('Doing things')
   
    CliUx.ux.action.stop(`Done!`)
  }
}

Expected behavior
To be able to import the package as described in the documentation. Unsure if I'm missing something? I've looked into the core package and I can't see it, has it been removed?

Screenshots
image

Environment (please complete the following information):

  • OS & version: macOS Monterey
  • WebStorm

Lodash error thrown when running `CliUx.ux.open`

Hi, I'm receiving the following error with the lodash module whenever I run CliUx.ux.open:

/Users/***/Projects/cliux-test/node_modules/@oclif/core/lib/cli-ux/open.js:62
            if (lodash_1.default.isNumber(code) && code > 0) {
                                 ^

TypeError: Cannot read property 'isNumber' of undefined
    at ChildProcess.<anonymous> (/Users/***/Projects/cliux-test/node_modules/@oclif/core/lib/cli-ux/open.js:62:34)
    at Object.onceWrapper (events.js:421:26)
    at ChildProcess.emit (events.js:314:20)
    at maybeClose (internal/child_process.js:1022:16)
    at Socket.<anonymous> (internal/child_process.js:444:11)
    at Socket.emit (events.js:314:20)
    at Pipe.<anonymous> (net.js:675:12)

The following code will reproduce the error:

import {CliUx} from "@oclif/core";

const run = async () => {
  await CliUx.ux.open("https://www.google.com");
};

run().catch((err) => console.error(err));

Please note that the error goes away if I remove the .default from the affected line in open.js as follows:

if (lodash_1.isNumber(code) && code > 0) {

I was able to reproduce this on Node versions 12, 14 and 16. Is this a bug or am I importing modules incorrectly?

[BUG] exclusive flag option doesn't work with negated flag (allowNo)

Behavior

Flags config

watermelon: flags.boolean({ default: true, allowNo: true }),
apple: flags.string({ exclusive: ['no-watermelon'] })

Execution

> cmd --no-watermelon --apple
[executes]

Expected Behavior

> cmd --no-watermelon --apple
Error: --apple= cannot also be provided when using --no-watermelon=

Error: command X not found

Hi, we have migrated our oclif CLI to the core version following the migration guide

Locally it seems works as expected, I'm be able to run all the commands. But when I install the global version it doesn't work, does not found any command, specifically I have the following error message: "Error: command x not found"

I have these dependencies installed:

dependencies:

{
    "@oclif/core": "^1.1.2",
    "@oclif/plugin-help": "^5.1.10",
    "@oclif/plugin-plugins": "^2.0.12"
 } 

And these are the oclif settings:

oclif:

{
    "commands": "./lib/commands",
    "bin": "test",
    "plugins": [
      "@oclif/plugin-help",
      "@oclif/plugin-plugins"
    ],
    "topicSeparator": " ",
    "additionalHelpFlags": [
      "-h"
    ],
    "additionalVersionFlags": [
      "-v"
    ]
}

Do you have any clue that it can't be happening?

Possibly remove lodash from dependencies

We've been using oclif for https://github.com/expo/eas-cli and it works really great! One thing we try to achieve in our CLI is the minimal package size. I mean we keep an eye on dependencies that we add to our package.json. One of the dependencies we especially try to avoid is lodash. It weighs around 5 MB:

root@927d4ac7c00b:~/aaa# du -h --max-depth 1 node_modules/ | grep lodash
68K     node_modules/lodash.merge
5.0M    node_modules/lodash

Unfortunately, lodash is one of the @oclif/core dependencies. However, it seems it's not used in many places - https://github.com/oclif/core/search?q=lodash

From what I can see you only use two functions:

The first one should be pretty straightforward to re-implement. The second is not that easy to replace (but I'm not familiar with the codebase). I also saw this PR caaff0b where you replaced lodash.template with lodash and I'm quite not sure why because the PR description doesn't say too much about the motivation.

Do you think you could make oclif even better and get rid of lodash from dependencies? If yes, I'm a volunteer to work on the pull request.

Could not resolve "../screen"

Description

Encountered issue when bundling with esbuild:

Error: Build failed with 1 error:
../../../common/temp/node_modules/.pnpm/@[email protected]/node_modules/@oclif/core/lib/errors/errors/cli.js:40:38: error: Could not resolve "../screen"
  ...20 lines omitted...
        suggestion: ''
      },
      notes: [],
      pluginName: '',
      text: 'Could not resolve "../screen"'
    }
  ],
  warnings: []
}
ERROR: "build:esbuild" exited with 1.

Cause

Is there a reason you guys are mixing inline requires with import syntax? I've found this can cause a lot of issues in the past and can't see any obvious benefit?

Examples

The last link is specifically the line that throws the error described, but I imagine there will be a lot of others once this is resolved if this one has managed to get through your CI / tests given that the ../screen file doesn't exist.

See here for ../ directory (note the screen file exists in the next level up):

This would be avoided by enforcing typescript-eslint/no-require-imports with a hard error during build.

These inline requires are not handled well by the tsc compiler whereas using top of page imports will have definitely failed to build, alerting you to the error much sooner before publishing.

Context

  • @oclif/core 1.0.2
  • esbuild 0.13.9
  • typescript 4.4.3

Use `module: node12` in tsconfig.json; supports not transpiling `import() / dynamic import` for CommonJS code

A most recent merged PR for Typescript now better supports Node 12. In particular there is no need for the "semi-hack" used in module-loader.ts.

You can see discussion here and the PR here. When the latest Typescript version is available w/ this PR I'll look into adding module: node12 to tsconfig.json and verify that import dynamic works correctly with the Oclif core build and remove the workaround.

A blog post about the Typescript 4.5 beta.

Command parsing hangs under unit test

We have been using oclif v1 and unit test our CLI using jest. We follow the instruction in the oclif docs to use the static Command.run inside tests. When attempting to migrate the project from v1 to core, several of the tests had timeout failures which signified a hanging handler. I dug into the parsing differences between v1 and core and found that core has a new feature which falls back to reading from stdin if there are no arguments specified. This causes issues as our CLI has several commands which have only optional arguments. I am not aware of this being a known breaking change, as it isn't mentioned on the migration guide.

Bug

I believe a summary of the issue is that calling Command.run() without args under test causes a hang at this line in the parser (waiting for input that will never arrive).

Current (core) behavior

Here is an example of one of our tests that is hanging with core.

await expect(AppRegisterCommand.run()).resolves.not.toThrow() // note the omitted run args

It fails with the following.

: Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Timeout

The test just below it however passes on the same command since it includes args.

await expect(AppRegisterCommand.run([appId])).resolves.not.toThrow() // including any arg works around the hang

โœ• calls selectFromList with correct config (5005 ms)
โœ“ calls correct endpoint for registration and logs success (18 ms)

Environment

"@oclif/core": "^1.0.11 "
darwin-x64 node-v12.18.1

The commands should be imported/declared after the `init` hook

Do you want to request a feature or report a bug?

bug

What is the current behavior?

The commands are being imported/declared before the init hook.

I'm using i18next to provide translations and proper internationalization to my CLI. It gives the t function that returns a translation for the received key, but it only works after i18next is initialized. i18next initializes by calling its async method init with some configuration.

I tried to initialize i18next in the init hook, but the init hook executes after importing commands. And since commands are classes, their static properties are evaluated when the module is loaded, and using the t function won't work.

class Example extends Command {
  static description = t('commands.example.description');

  // ...
}

In the snippet above Example.description value will be undefined because i18next isn't initialized.

What is the expected behavior?

The commands should be imported/declared after the init hook.

  • I'm using macOS M1 Pro, but the same happens in Debian (under Windows 11 WSL2).
  • I'm using @oclif/core version 1.3.2.

Hooks: unable to stop command execution if error occurs

Executing this.error() or this.exit() inside an hook of any type (init, prerun ...) the terminal does not show any error message and the command is run ignoring the Error.

I've noticed this behavior after the migration from v1 to v2 and using the new package @oclif/core.
Is this a bug or has changed the way to interrupt command from inside hooks in case of errors?

Thanks!

Migrate single command cli to @oclif/core

Hi guys, I'm trying to migrate bindl, but I'm having a really hard time.

It's a single command CLI (i.e. just calling bindl would do the trick), but after following all the advices in the migration guide, it simply does not work.

Am I missing something?

# Before
โฏ bin/run
 โ€บ   Error: I was not able to load the configuration file.

# After
โฏ bin/dev
Downloads and extracts binaries from compressed packages using a config file

VERSION
  bindl/2.0.0 wsl-x64 node-v16.15.1

USAGE
  $ bindl [COMMAND]

[Proposal] Option to ensure plugin hooks are executed AFTER the root CLI's hooks

We have a root CLI set up with a handful of plugin-plugins that can be installed alongside it. Our root CLI currently has its own init hooks that need be executed.

We now want to implement some init hooks within our plugin-plugins. BUT, we want to ensure any plugin hooks are executed after the root CLI's hooks, because some of them are dependent on the setup that our root CLI provides.

In the short-term, we will likely implement a custom event (maybe called plugins:init) that plugins can use to ensure they are executed after the root CLI has initialized. There are two downsides to this approach from what I can see:

  1. Plugins can no longer be utilized standalone - they will always expect the root CLI to trigger the custom event.
  2. Custom business logic - Since we are using a custom event with this approach, there's a maintenance cost associated with documenting usage of this custom event and it will likely be hard to search for if problems arise.

I think it would be a bit cleaner to allow an option that ensures order is preserved. Is this a possible feature that could be implemented?

Loop when reading hidden prompt from /dev/null

A prompt with {type: "hide"} option loops forever when input is redirected from /dev/null.

To reproduce, create a program like:

const {CliUx} = require('@oclif/core')
CliUx.ux.prompt('Enter input', {type: "hide"})

Then run it taking stdin from /dev/null. Or run npm i and then ./prompt.js < /dev/null in the attached project:
promptloop.zip

Actual behavior: the prompt loops forever.
Expected behavior: the prompt completes as no data is read (as it does without {type: "hide"}).

Feature Request: Better error handling

Feature Request

What is the current behavior?

Being faced with errors is usual, but oclif's errors are not very helpful most of the time.

What is the expected behavior?

Either expose the root of the error more clearly. For instance I'm updating my CLI from v1 right now, and one of the subcommands is buggy and I really can't find any infos... here is the output (not asking for support on this I'll open a separate issue if needed, just to prove my point):

Running my cli subcommand:

DEBUG=* mtb resolve-to-nuke --help

  config reading core plugin ~/mtb-cli +0ms
  config loadJSON ~/mtb-cli/package.json +0ms
  config loadJSON ~/mtb-cli/oclif.manifest.json +0ms
  config loadJSON ~/mtb-cli/.oclif.manifest.json +0ms
  config:@mtb/cli loading IDs from ~/mtb-cli/dist/commands +0ms
  config:@mtb/cli found commands [ 'mono', 'resolve-to-nuke', 'mkthumbnail' ] +20ms
  config:@mtb/cli (require) ~/mtb-cli/dist/commands/mono +15ms
  config reading user plugins pjson /Users/mel/.local/share/mtb/package.json +0ms
  config loadJSON /Users/mel/.local/share/mtb/package.json +0ms
(node:82496) [MODULE_NOT_FOUND] ModuleLoadError Plugin: @mtb/cli: [MODULE_NOT_FOUND] require failed to load ~/mtb-cli/dist/commands/resolve-to-nuke
module: @oclif/core@1.6.1
task: toCached
plugin: @mtb/cli
root: ~/mtb-cli
See more details with DEBUG=*
(Use `node --trace-warnings ...` to show where the warning was created)
ModuleLoadError Plugin: @mtb/cli: [MODULE_NOT_FOUND] require failed to load ~/mtb-cli/dist/commands/resolve-to-nuke
    at Function.loadWithData (~/node_modules/.pnpm/@oclif+core@1.6.1/node_modules/@oclif/core/lib/module-loader.js:85:23)
    at fetch (~/node_modules/.pnpm/@oclif+core@1.6.1/node_modules/@oclif/core/lib/config/plugin.js:181:83)
    at Plugin.findCommand (~/node_modules/.pnpm/@oclif+core@1.6.1/node_modules/@oclif/core/lib/config/plugin.js:197:27)
    at ~/node_modules/.pnpm/@oclif+core@1.6.1/node_modules/@oclif/core/lib/config/plugin.js:234:73
    at Array.map (<anonymous>)
    at Plugin._manifest (~/node_modules/.pnpm/@oclif+core@1.6.1/node_modules/@oclif/core/lib/config/plugin.js:232:58)
    at async Plugin.load (~/node_modules/.pnpm/@oclif+core@1.6.1/node_modules/@oclif/core/lib/config/plugin.js:128:25)
    at async Config.load (~/node_modules/.pnpm/@oclif+core@1.6.1/node_modules/@oclif/core/lib/config/config.js:86:9)
    at async Function.load (~/node_modules/.pnpm/@oclif+core@1.6.1/node_modules/@oclif/core/lib/config/config.js:80:9)
    at async Object.run (~/node_modules/.pnpm/@oclif+core@1.6.1/node_modules/@oclif/core/lib/main.js:44:20)
module: @oclif/core@1.6.1
task: toCached
plugin: @mtb/cli
root: ~/mtb-cli
See more details with DEBUG=*
  config loading plugins [
  '@oclif/plugin-help',
  '@oclif/plugin-plugins',
  '@oclif/plugin-autocomplete'
] +4ms
  config reading core plugin ~/mtb-cli/node_modules/@oclif/plugin-autocomplete +0ms
  config loadJSON ~/mtb-cli/node_modules/@oclif/plugin-autocomplete/package.json +0ms
  config reading core plugin ~/node_modules/.pnpm/@oclif+plugin-help@5.1.12/node_modules/@oclif/plugin-help +0ms
  config loadJSON ~/node_modules/.pnpm/@oclif+plugin-help@5.1.12/node_modules/@oclif/plugin-help/package.json +0ms
  config reading core plugin ~/node_modules/.pnpm/@oclif+plugin-plugins@2.1.0/node_modules/@oclif/plugin-plugins +0ms
  config loadJSON ~/node_modules/.pnpm/@oclif+plugin-plugins@2.1.0/node_modules/@oclif/plugin-plugins/package.json +0ms
  config loadJSON ~/mtb-cli/node_modules/@oclif/plugin-autocomplete/oclif.manifest.json +0ms
  config loadJSON ~/node_modules/.pnpm/@oclif+plugin-help@5.1.12/node_modules/@oclif/plugin-help/oclif.manifest.json +0ms
  config loadJSON ~/node_modules/.pnpm/@oclif+plugin-plugins@2.1.0/node_modules/@oclif/plugin-plugins/oclif.manifest.json +0ms
  config:@oclif/plugin-autocomplete using manifest from ~/mtb-cli/node_modules/@oclif/plugin-autocomplete/oclif.manifest.json +0ms
  config:@oclif/plugin-help using manifest from ~/node_modules/.pnpm/@oclif+plugin-help@5.1.12/node_modules/@oclif/plugin-help/oclif.manifest.json +0ms
  config:@oclif/plugin-plugins using manifest from ~/node_modules/.pnpm/@oclif+plugin-plugins@2.1.0/node_modules/@oclif/plugin-plugins/oclif.manifest.json +0ms
  config config done +33ms
  config start init hook +0ms
  config init hook done +1ms
 โ€บ   Error: Command resolve-to-nuke not found.

cliUx.ux.flush timeout throws after 10 seconds with `Error: timed out`

Describe the bug

When there's a long running process using stdout, this will throw after 10s and break it:

return Promise.race([p, wait(ms, true).then(() => ux.error('timed out'))])

Before version 1.9.7 it would silently catch it and let it continue:
v1.9.6...v1.9.7

To Reproduce

Concrete example:
We're using listr2 task inside a command to spawn process that takes a while to finish (compiling a rust project).
It pipes the stdout into task.stdout().
After 10 seconds, the process exits with an Error: timed out

Expected behavior

The process is not interrupted after 10 seconds.

Screenshots
Screenshot 2022-07-25 at 19 21 35

Environment (please complete the following information):

  • MacOS Monterey v12.4
  • zsh 5.7.1 (x86_64-apple-darwin18.2.0) on iTerm2

Additional context

https://github.com/AstarNetwork/swanky-cli/blob/master/src/commands/compile/index.ts

CliUx sort throw "TypeError: this.options.sort.split is not a function"

Hello,

I have recently upgraded my oclif based cli from cli-ux 5.6.3 to 6.0.9.
Since then, I get the error mentioned in the issue title.

TypeError: this.options.sort.split is not a function

Since https://github.com/oclif/cli-ux is deprecated in favor of oclif/core, I have followed the migration steps as instructed here.
Unfortunately, I still get the same error.

Context

  • Javascript cli built with oclif
  • Usage of cli.table/CliUx.ux.table
  • Error is thrown while using the --sort flag.

Thanks for your help ๐Ÿ™๐Ÿป
PS: Tell me any other information I can provide you to debug this error.

display min/max in help

some types (number, integer, etc) have min/max values.

It would be nice if the help output displayed those.

ES Module support for Oclif v2

Edit (06/01/21): Thanks to the Oclif team for letting me contribute ESM support to Oclif v2! I have updated all links in this issues comments to be as accurate as possible. Please see this comment on a current workaround to publish an ESM CLI while the rest of the Oclif v2 infrastructure is updated.

Hi Philipe & Thomas (@RasPhilCo / @amphro) et al,

Great work on Oclif v2 Philipe and all involved. I really like the refactor to a core module compared to v1. Removing the circular dependencies (IE between v1 plugin-help and plugin-command) helps a bunch. I would like to discuss adding ES Module (ESM) support to Oclif v2 before initial launch in ~June. I initially prototyped the changes in v1 and saw the core announcement and have fully worked out the essential changes to @oclif/core for v2. I have a fork of @oclif/core with these changes and have fully implemented non-trivial Oclif middleware and resulting CLI w/ ESM to test everything out that is published on NPM using my @oclif/core fork. I also have three test repositories that have stripped down CLI tests with variations that respectively cover a wide swath of the 12.0.0 to 15.x Node ecosystem by Github Actions. I'll comment on this below in another post.

This kind of fundamental change is best in a new version of Oclif and is timely due to the minimum Node support of v12 for Oclif v2 which is the first Node LTS version that has wider support for ES Modules. Supporting ESM now future proofs Oclif as the larger Node ecosystem transitions to ESM in the coming years. There is a fair amount of movement in this direction already in April / May as Node 12 becomes the minimum LTS release. These changes do not affect current support of CommonJS / Typescript CLIs. In fact there could be some benefit for end user CLI development in Typescript as one could target ESM instead of CJS and if launching a CLI w/ Node 14 as a minimum version for support can target ES2020 for Typescript or simply write the CLI in ESM / ES2020. The main core Oclif v2 code should still of course target CJS for now, but this frees up end users to choose their target of choice between CJS / ESM / Typescript.

I will go over in more detail in a follow up post to this issue regarding what the changes cover and note the other areas of the larger codebase / other modules that require changes. I have already gone through several iterations of improving the essential changes and have arrived at what I think is a clean addition which I have tested across all the OS platforms w/ Node 12.0.0 - 15.x. Beyond just submitting the pull requests / changes I am fully committed to helping write new tests / verifying Node 12.0.0 compatibility and assisting with writing documentation changes for v2. IE not just provide a code dump and add extra burden to any internal team members before the v2 launch. I believe there is enough time to get these changes in responsibly before the v2 ~June launch.

Instead of just making a pull request to @oclif/core I'd like to discuss willingness to add ESM support through my involvement first. In follow up posts to this issue I'll discuss the essential changes to @oclif/core and other modules (plugin-help / dev-cli / oclif / test). I'm 100% open to modifying anything necessary before / while making any pull requests. I look forward to working with the Oclif team to support ESM and make Oclif v2 a very solid solution for advanced CLI development on Node now and into the future.

Cannot find module ESM and Commands not loading properly

Hey folks, I have the following setup, https://github.com/straw-hat-team/cli/tree/master/packages/cli which I am trying to convert to ESM

But I keep getting the following:

โžœ  react-hooks git:(master) DEBUG=* node ./node_modules/@straw-hat/cli/bin/run.js help
node:internal/process/esm_loader:94
    internalBinding('errors').triggerUncaughtException(
                              ^

Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/Users/ubi/Developer/github.com/straw-hat-team/cli/node_modules/@oclif/core/flush' imported from /Users/ubi/Developer/github.com/straw-hat-team/cli/packages/cli/bin/run.js
Did you mean to import @oclif/core/flush.js?
    at new NodeError (node:internal/errors:371:5)
    at finalizeResolution (node:internal/modules/esm/resolve:416:11)
    at moduleResolve (node:internal/modules/esm/resolve:932:10)
    at defaultResolve (node:internal/modules/esm/resolve:1044:11)
    at ESMLoader.resolve (node:internal/modules/esm/loader:422:30)
    at ESMLoader.getModuleJob (node:internal/modules/esm/loader:222:40)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:76:40)
    at link (node:internal/modules/esm/module_job:75:36) {
  code: 'ERR_MODULE_NOT_FOUND'
}

I fixed this by adding the extension to the import (is that required now?)

#!/usr/bin/env node

import oclif from '@oclif/core';
import flush from '@oclif/core/flush.js';
import handle from '@oclif/core/handle.js';

oclif.run().then(flush).catch(handle);

But then the commands don't seem to be working properly

โžœ  react-hooks git:(master) DEBUG=* node ./node_modules/@straw-hat/cli/bin/run.js help
  config reading core plugin /Users/ubi/Developer/github.com/straw-hat-team/cli/node_modules/@oclif/core +0ms
  config loadJSON /Users/ubi/Developer/github.com/straw-hat-team/cli/node_modules/@oclif/core/package.json +0ms
  config loadJSON /Users/ubi/Developer/github.com/straw-hat-team/cli/node_modules/@oclif/core/oclif.manifest.json +0ms
  config loadJSON /Users/ubi/Developer/github.com/straw-hat-team/cli/node_modules/@oclif/core/.oclif.manifest.json +0ms
  config reading user plugins pjson /Users/ubi/.local/share/@oclif/core/package.json +0ms
  config loadJSON /Users/ubi/.local/share/@oclif/core/package.json +0ms
  config config done +0ms
  config start init hook +0ms
  config init hook done +1ms
  config runCommand help [] +0ms
  config start command_not_found hook +1ms
  config command_not_found hook done +0ms
 โ€บ   Error: command help not found
โžœ  react-hooks git:(master)

Any idea of what the proper setup suppose to be for an ESM project? I can't find an example project of it.

Thanks in advanced,

Intended approach for env vars?

I am unsure about the correct way of consuming secrets for some services. I want to provide a github api token to my oclif app, but have not found any documentation on this. I was considering using dotenv (with their dotenv.config() syntax) but am unsure if this will work when I generate binaries.

Any help would be awesome

`Flags.integer()` isn't parsed properly when `required: true`

Describe the bug
When we bump v1.13.0 to v1.13.4, TypeScript starts to complain that our Flags.integer() with required: true is parsed as number | undefined (not number) and can't be assigned to number value. Previously, it seems the parsed flags had any type and TypeScript didn't complain. However, now it has parsed types but wrong type for Flags.integer() with required: true.

See annotations in this PR: https://github.com/autifyhq/autify-cli/pull/50/files

Note: Flags.string() with required: true is parsed properly and has string type.

To Reproduce

  static flags = {
    "test-plan-id": Flags.integer({
      required: true,
    }),
    "create-url-replacement-request": Flags.string({
      required: true,
    }),
  };

  public async run(): Promise<void> {
    const { flags } = await this.parse(WebApiCreateUrlReplacement);
    // flags["test-plan-id"] => number | undefined
    // flags["create-url-replacement-request"] => string

Expected behavior

  static flags = {
    "test-plan-id": Flags.integer({
      required: true,
    }),
    "create-url-replacement-request": Flags.string({
      required: true,
    }),
  };

  public async run(): Promise<void> {
    const { flags } = await this.parse(WebApiCreateUrlReplacement);
    // flags["test-plan-id"] => number
    // flags["create-url-replacement-request"] => string

Screenshots
With v1.13.4:
Screen Shot 2022-08-08 at 10 21 02 AM

With v1.13.0:
Screen Shot 2022-08-08 at 10 31 58 AM

Environment (please complete the following information):

  • OS & version: Linux 559abc3f4f3f 5.15.0-43-generic #46~20.04.1-Ubuntu SMP Thu Jul 14 15:20:17 UTC 2022 x86_64 GNU/Linux
  • Shell/terminal & version: GNU bash, version 5.1.4(1)-release (x86_64-pc-linux-gnu)

Additional context
N/A

Tests run against lib, not src

Hi,

After we bumped our CLI to @oclif/core, the tests are no longer running against the src/ folder. Instead, they are using the transpiled JavaScript from the lib folder.

This is absolutely fine for testing basic commands. However, mocking modules no longer works if the tested commands are coming from the lib/ and not the src/ folder.

When the tests are running, the environment variable NODE_ENV is set to test. Which will cause the method in src/util.ts#isProd to always always return true when running the tests, and therefore use the lib/ instead of the src/ folder.

Since I am not sure this condition was purposefully designed like this, I manually set NODE_ENV to development just before running the tests.

Should the method isProd() also check if the NODE_ENV is not equal to test?

Thanks,

FYI, we are using Jest instead of mocha.

ux.table Maximum call stack size exceeded

Describe the bug
table throwing with large number of rows

To Reproduce
ORG_MAX_QUERY_LIMIT=1000000 ./bin/dev force:data:soql:query -u hub -q 'select id from scratchOrgInfo'

Expected behavior
print a table

Screenshots
error is ```
โžœ plugin-data git:(cd/wait-end) โœ— ./bin/dev force:data:soql:query -u hub -q 'select id from scratchOrgInfo' > output.txt
Querying Data... done
ERROR running force:data:soql:query: Maximum call stack size exceeded


**Environment (please complete the following information):**
zsh 5.8.1 (x86_64-apple-darwin21.0)

**Additional context**
running a debugger--everything is fine until the call to `ux.table`

@oclif/core cannot load index.js files when using ES modules

I started converting my @oclif/core project from Common.js to ES modules, but unfortunately I reached a blocking point. It seems @oclif/core cannot load any of the index.js files under the commands folder. When running the CLI it prints the following error:

(node:49740) [ERR_REQUIRE_ESM] Error Plugin: @ngls/cli [ERR_REQUIRE_ESM]: require() of ES Module /Users/rpastro/ngls/ngls/cli/src/commands/elements/index.js from /Users/rpastro/ngls/ngls/cli/node_modules/@oclif/core/lib/module-loader.js not supported.
Instead change the require of index.js in /Users/rpastro/ngls/ngls/cli/node_modules/@oclif/core/lib/module-loader.js to a dynamic import() which is available in all CommonJS modules.
module: @oclif/[email protected]
task: toCached
plugin: @ngls/cli
root: /Users/rpastro/ngls/ngls/cli

All other files are successfully loaded by @oclif/core. The problem is only with the index.js files.

Looking into the code, the problem seems to be in the ModuleLoader.resolvePath function. The modulePath parameter passed in case of index.js is the folder name. For instance, if the command file is ".../commands/users/index.js", modulePath is set to ".../commands/users". Since this is a valid directory, fs.existsSync(filePath) returns true and ModuleLoader.isPathModule returns false since there is no extension in ".../commands/users" and we fall into the default branch.

tables have `undefined` in them

Describe the bug
cli.ux.table is printing the words undefined and null and it'd be prettier if it was just left out

To Reproduce
pass objects to table with out

Expected behavior
omit undefined/null by default

Screenshots
Screen Shot 2022-06-23 at 1 32 32 PM

Environment (please complete the following information):
darwin-x64 node-v16.15.1

TypeError: this.jsonEnabled is not a function

OS: mac os 12.2.1
Node Version: v16.13.1
CPU: arm64

The code of core/src/command.ts is transpiled in JS in the following:

   log(message = '', ...args) {
        if (!this.jsonEnabled()) {
            // tslint:disable-next-line strict-type-predicates
            message = typeof message === 'string' ? message : (0, util_1.inspect)(message);
            process.stdout.write((0, util_1.format)(message, ...args) + '\n');
        }
    }
    jsonEnabled() {
        return this.ctor.enableJsonFlag && this.argv.includes('--json');
    }

The above code causes the following error:

TypeError: this.jsonEnabled is not a function

Is there a way to fix it?

oclif rejects empty string given for required argument

Do you want to request a feature or report a bug?

Report a bug

What is the current behavior?

oclif rejects an empty string argument when a string argument is requried

Example code:

import {Command, flags} from '@oclif/command'

class Foo extends Command {
  static description = 'describe the command here'

  static args = [{name: 'file', required: true}]

  async run() {
    const {args, flags} = this.parse(Foo)
  }
}

export = Foo

Example output:

$ ./bin/run
 โ€บ   Error: Missing 1 required arg:
 โ€บ   file
 โ€บ   See more help with --help
$ ./bin/run ''
 โ€บ   Error: Missing 1 required arg:
 โ€บ   file
 โ€บ   See more help with --help
$ ./bin/run 'foo'
$

The first error is expected (we omitted the argument). The second is not (we supplied an empty string as argument). In the 3rd case, the command succeeds as expected.

What is the expected behavior?

An empty string argument should be accepted.

I'm using oclif/command 1.8.0 on Ubuntu 20.04.

Table filter to allow expressions

Is your feature request related to a problem? Please describe.

I'm just getting started with oclif and am already really enjoying it. However I find myself wanting more. Specifically I like the filter flag for the table, but don't seem to be able filter on anything other than a string contains. I looked at the source code for this, but I am also new to TypeScript so, while I'm not 100% sure I'm not just making a mistake, I don't think the code has this capability, and at the moment it's probably a little over my head to make the PR for this functionality myself.

Describe the solution you'd like

$ my-cli command --filter "forks>=1"
Name              Forks
 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”€โ”€โ”€โ”€โ”€
 gene-actions-test 1
 gene-test         2

Describe alternatives you've considered

I haven't found anything else at the moment. I could always preprocess that content, but in my mind defeats the purpose of having these handy flags.

Additional context

none

Support logging to stderr

Command has a log() method for logging to stdout. Part of being a well-behaved CLI program is only logging stuff to stdout that might be consumed by another process, e.g. fed into another program's stdin. That means errors and other debug/status info should be printed to stderr. It would be nice if oclif commands supported this like they do with stdout via this.log()

As it stands, I have to use process.stderr (which is what Command.log is doing under the hood for stdout)

Aliases with topic Separator set to a space doesn't work.

Question, I have configured the topicSeparator with spaces.

My aliases look like the following:

static aliases = ['ls:policy', 'list:policy']

And the commands in the documentation are generated as expected:

image

If I run an alias command without params it works well. That is: oex ls policy or oex list policy

But if I try to execute any of the two commands with parameters, for example: oex ls policy aws. I get the error: command ls:policy:aws not found.

Instead this does work by executing the following command: oex ls:policy aws (as in oclif v1)

Could this be due to a problem in my configuration or is it a bug in oclif v2?

Bug resolving commandsDir in child_process

Heya!

I think I have a very specific usecase, I have a oclif cli app and to make the publication process a little easier, I created another oclif cli app which is responsible for running the chain of commands necessary for publication to S3 and promoting the release.

I run the commands in execa. I tried child_process.exec() as well as qqjs which uses execa under the hood.

Whenever the oclif manifest command gets called in yarn prepack, the entire oclif pack tarballs command fails, since the commandDir is resolved to /src/commands instead of /dist/commands like it should. I discovered this by adding a console log into plugin.js into the get commandsDir() function.

    get commandsDir() {
			console.log(this.pjson.name, ':', this.pjson.oclif.commands,(0, ts_node_1.tsPath), this.root, (0, ts_node_1.tsPath)(this.root, this.pjson.oclif.commands));
        return (0, ts_node_1.tsPath)(this.root, this.pjson.oclif.commands);
    }

The this.pjson.name, this.pjson.oclif.commands and this.root get resolved properly, but instead of resolving to what the this.pjson.oclif.commands returns, it resolves to /src/commands

Console log here:

oclif : ./lib/commands [Function: tsPath] /Users/steps/Projects/CLIS/ddm-cli/node_modules/oclif /Users/steps/Projects/CLIS/ddm-cli/node_modules/oclif/lib/commands
ddm-cli : ./dist/commands [Function: tsPath] /Users/steps/Projects/CLIS/ddm-cli /Users/steps/Projects/CLIS/ddm-cli/src/commands
ddm-cli : ./dist/commands [Function: tsPath] /Users/steps/Projects/CLIS/ddm-cli /Users/steps/Projects/CLIS/ddm-cli/src/commands
ddm-cli : ./dist/commands [Function: tsPath] /Users/steps/Projects/CLIS/ddm-cli /Users/steps/Projects/CLIS/ddm-cli/src/commands
ddm-cli : ./dist/commands [Function: tsPath] /Users/steps/Projects/CLIS/ddm-cli /Users/steps/Projects/CLIS/ddm-cli/src/commands
ddm-cli : ./dist/commands [Function: tsPath] /Users/steps/Projects/CLIS/ddm-cli /Users/steps/Projects/CLIS/ddm-cli/src/commands
ddm-cli : ./dist/commands [Function: tsPath] /Users/steps/Projects/CLIS/ddm-cli /Users/steps/Projects/CLIS/ddm-cli/src/commands
ddm-cli : ./dist/commands [Function: tsPath] /Users/steps/Projects/CLIS/ddm-cli /Users/steps/Projects/CLIS/ddm-cli/src/commands
ddm-cli : ./dist/commands [Function: tsPath] /Users/steps/Projects/CLIS/ddm-cli /Users/steps/Projects/CLIS/ddm-cli/src/commands
ddm-cli : ./dist/commands [Function: tsPath] /Users/steps/Projects/CLIS/ddm-cli /Users/steps/Projects/CLIS/ddm-cli/src/commands
ddm-cli : ./dist/commands [Function: tsPath] /Users/steps/Projects/CLIS/ddm-cli /Users/steps/Projects/CLIS/ddm-cli/src/commands
ddm-cli : ./dist/commands [Function: tsPath] /Users/steps/Projects/CLIS/ddm-cli /Users/steps/Projects/CLIS/ddm-cli/src/commands
ddm-cli : ./dist/commands [Function: tsPath] /Users/steps/Projects/CLIS/ddm-cli /Users/steps/Projects/CLIS/ddm-cli/src/commands
ddm-cli : ./dist/commands [Function: tsPath] /Users/steps/Projects/CLIS/ddm-cli /Users/steps/Projects/CLIS/ddm-cli/src/commands
ddm-cli : ./dist/commands [Function: tsPath] /Users/steps/Projects/CLIS/ddm-cli /Users/steps/Projects/CLIS/ddm-cli/src/commands
ddm-cli : ./dist/commands [Function: tsPath] /Users/steps/Projects/CLIS/ddm-cli /Users/steps/Projects/CLIS/ddm-cli/src/commands
ddm-cli : ./dist/commands [Function: tsPath] /Users/steps/Projects/CLIS/ddm-cli /Users/steps/Projects/CLIS/ddm-cli/src/commands
ddm-cli : ./dist/commands [Function: tsPath] /Users/steps/Projects/CLIS/ddm-cli /Users/steps/Projects/CLIS/ddm-cli/src/commands
ddm-cli : ./dist/commands [Function: tsPath] /Users/steps/Projects/CLIS/ddm-cli /Users/steps/Projects/CLIS/ddm-cli/src/commands
ddm-cli : ./dist/commands [Function: tsPath] /Users/steps/Projects/CLIS/ddm-cli /Users/steps/Projects/CLIS/ddm-cli/src/commands
ddm-cli : ./dist/commands [Function: tsPath] /Users/steps/Projects/CLIS/ddm-cli /Users/steps/Projects/CLIS/ddm-cli/src/commands
ddm-cli : ./dist/commands [Function: tsPath] /Users/steps/Projects/CLIS/ddm-cli /Users/steps/Projects/CLIS/ddm-cli/src/commands

Is there any way to fix this behaviour? The tsPath function resolves properly, the root path is proper, the oclif.commands is proper but for whatever reason tsPath is misbehaving

Also my exec function is

async exec(command: string, cwd: string): Promise<ExecaChildProcess> {
		const options = {
			shell: '/bin/zsh',
			cwd,
			env: process.env,
			preferLocal: true,
		};

		return execa.command(command, options);
	}

Help does not display env name when configured for a parameter

Do you want to request a feature or report a bug?

Feature

Similar to how flags default are displayed in the help for a command, the help could also display the name of the environment variable when configured for a flag.

What is the current behavior?

bin\run <command> --help displays for each flags the default if configured, eg:

--my-flag=my-flagย ย ย ย [default: my-default] Flag for ...

To find out the environment variable name that can be used instead of a flag, one must look at the code, or add manually the variable name to the flag's help.

What is the expected behavior?

Help could include the name of the environment variable, eg something like this:

--my-flag=my-flagย ย ย ย [default: my-default, env: MY_FLAG] Flag for ...

Yarn PnP

Hi, would like to know if there are any plans for Yarn PnP support?

I'm currently working on a monorepo which uses yarn workspaces and PnP which is great. But I am not able to make oclif work.

While Yarn PnP is not yet supported, is there a possible hack/workaround to get oclif working using PnP mode?

Remaining Work

As we announced in this March blog post, weโ€™re working on v1 of the new @oclif/core library, which we hope to release this summer.

Weโ€™ve pinned this issue to keep the community up to date on the remaining work to complete v1 and to update the existing oclif plugins and packages to use core. Weโ€™ll update this post as work is completed or new work is added.

Remaining @oclif/core work

Migrating packages and plugins to @oclif/core [DONE]

Repository Archive Repo Determine If Still Requried Add maitenance notification Bump Engine to Node 14 Ensure testing Node 14, 16 & latest & windows Bump to typescript 4 Migrate to Core Major version bump
@oclif/plugin-autocomplete n/a n/a n/a required required required required required
@oclif/plugin-command-snapshot n/a n/a n/a required required required required required
@oclif/plugin-commands n/a n/a n/a required required required required required
@oclif/plugin-help n/a n/a n/a required required required required required
@oclif/plugin-interceptor n/a n/a n/a required required required required required
@oclif/plugin-not-found n/a n/a n/a required required required required required
@oclif/plugin-plugins n/a n/a n/a required required required required required
@oclif/plugin-update n/a n/a n/a required required required required required
@oclif/plugin-warn-if-update-available n/a n/a n/a required required required required required
@oclif/plugin-which n/a n/a n/a required required required required required
cli-ux n/a n/a n/a required required required required required
color n/a n/a n/a required required required required required
dev-cli n/a n/a required n/a n/a n/a n/a n/a
docker required n/a n/a n/a n/a n/a n/a n/a
eslint n/a n/a n/a required required required required required
eslint-config-oclif n/a n/a n/a required required required required required
eslint-config-oclif-typescript n/a n/a n/a required required required required required
fancy-test n/a n/a n/a required required required required required
fancy-test-nock n/a n/a n/a required required required required required
node-linewrap n/a n/a n/a required required required required required
nyc-config n/a n/a n/a required required required required required
oclif n/a n/a n/a required required required required required
screen n/a required n/a required required required required required
semantic-release n/a required n/a required required required required required
test n/a required n/a required required required required required
fixpack n/a required n/a required required required required required
open n/a required n/a required required required required required

@oclif/plugin-autocomplete

  • Update plugin-autocomplete to work with spaced commands
  • Update plugin-autocomplete to work on Windows

@oclif/plugin-help

  • Remove old --help logic

@oclif/plugin-not-found

  • Update plugin-not-found to work with spaced commands

@oclif/plugin-update

  • promote should throw an error when no targets are found
  • Make pack, promote, and upload commands public
  • Remove old s3 logic

All commands are required, slowing down the CLI

Do you want to request a feature or report a bug?

Bug

What is the current behavior?

I'm seeing all commands being required, both with ts-node and when using OCLIF_TS_NODE=0:

time DEBUG=* OCLIF_TS_NODE=0 npx mycli init --help
  @oclif/config reading core plugin /Users/matthewshwery/dev/src/github.com/<repo>/packages/cli +0ms
  @oclif/config loadJSON /Users/matthewshwery/dev/src/github.com/<repo>/packages/cli/package.json +0ms
  @oclif/config loadJSON /Users/matthewshwery/dev/src/github.com/<repo>/packages/cli/oclif.manifest.json +3ms
  @oclif/config loadJSON /Users/matthewshwery/dev/src/github.com/<repo>/packages/cli/.oclif.manifest.json +0ms
  @oclif/config:@mycli/cli loading IDs from /Users/matthewshwery/dev/src/github.com/<repo>/packages/cli/dist/commands +0ms
  @oclif/config:@mycli/cli found commands [ 'init', 'push', 'register', 'generate:action', 'generate:types' ] +10ms
  @oclif/config:@mycli/cli require /Users/matthewshwery/dev/src/github.com/<repo>/packages/cli/dist/commands/init.js +2ms
  @oclif/config:@mycli/cli require /Users/matthewshwery/dev/src/github.com/<repo>/packages/cli/dist/commands/push.js +317ms
  @oclif/config:@mycli/cli require /Users/matthewshwery/dev/src/github.com/<repo>/packages/cli/dist/commands/register.js +225ms
  @oclif/config:@mycli/cli require /Users/matthewshwery/dev/src/github.com/<repo>/packages/cli/dist/commands/generate/action.js +1ms
  @oclif/config:@mycli/cli require /Users/matthewshwery/dev/src/github.com/<repo>/packages/cli/dist/commands/generate/types.js +186ms
  @oclif/config reading user plugins pjson /Users/matthewshwery/.local/share/@mycli/cli/package.json +0ms
  @oclif/config loadJSON /Users/matthewshwery/.local/share/@mycli/cli/package.json +789ms
  @oclif/config loading plugins [ '@oclif/plugin-help' ] +3ms
  @oclif/config loadJSON /Users/matthewshwery/dev/src/github.com/<repo>/packages/cli/package.json +3ms
  @oclif/config loadJSON /Users/matthewshwery/dev/src/github.com/<repo>/packages/package.json +1ms
  @oclif/config reading core plugin /Users/matthewshwery/dev/src/github.com/<repo>/node_modules/@oclif/plugin-help +0ms
  @oclif/config loadJSON /Users/matthewshwery/dev/src/github.com/<repo>/node_modules/@oclif/plugin-help/package.json +0ms
  @oclif/config loadJSON /Users/matthewshwery/dev/src/github.com/<repo>/node_modules/@oclif/plugin-help/oclif.manifest.json +1ms
  @oclif/config:@oclif/plugin-help using manifest from /Users/matthewshwery/dev/src/github.com/<repo>/node_modules/@oclif/plugin-help/oclif.manifest.json +0ms
  @oclif/config config done +2ms
  @oclif/config start init hook +0ms
  @oclif/config init hook done +1ms
  mycli init version: @oclif/[email protected] argv: [ 'init', '--help' ] +0ms

Note: this happens across multiple commands, with and without --help

What is the expected behavior?

The documentation site claims that only the command to be executed will be required (emphasis mine):

The overhead for running an oclif CLI command is almost nothing. It requires very few dependencies (only 35 dependencies in a minimal setupโ€”including all transitive dependencies). Also, only the command to be executed will be required with node. So large CLIs with many commands will load just as fast as a small one with a single command.

=> Found "@oclif/[email protected]"
=> Found "@oclif/[email protected]"
=> Found "@oclif/[email protected]"
=> Found "@oclif/[email protected]"

On macOS 11.4 (Big Sur), tried on multiple Node versions (12x/14x/16x), Yarn 1.22

order of enableJsonFlag in code affects whether or not flags are registered

Describe the bug
Specifying enableJsonFlag after flags, causes the flags to not register. This worked fine in the last version I tried, 1.9.5, but has changed in 1.12.0.

To Reproduce
Steps to reproduce the behavior:

will only register the json flag

static flags = {
  test: Flags.string()
}
static enableJsonFlag = true

will register both flags

static enableJsonFlag = true
static flags = {
  test: Flags.string()
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.