oclif 4.5.7 β 4.6.1-dev.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -46
- package/lib/commands/generate/command.d.ts +3 -2
- package/lib/commands/generate/command.js +32 -9
- package/lib/commands/generate/hook.d.ts +2 -2
- package/lib/commands/generate/hook.js +42 -13
- package/lib/commands/generate.d.ts +57 -3
- package/lib/commands/generate.js +224 -11
- package/lib/commands/manifest.js +5 -8
- package/lib/commands/pack/macos.js +5 -5
- package/lib/commands/pack/tarballs.d.ts +1 -0
- package/lib/commands/pack/tarballs.js +7 -9
- package/lib/commands/pack/win.d.ts +1 -0
- package/lib/commands/pack/win.js +9 -10
- package/lib/commands/promote.js +12 -12
- package/lib/commands/readme.d.ts +1 -0
- package/lib/commands/readme.js +2 -2
- package/lib/commands/upload/deb.js +2 -2
- package/lib/commands/upload/macos.js +3 -3
- package/lib/commands/upload/tarballs.js +4 -7
- package/lib/commands/upload/win.js +3 -3
- package/lib/generator.d.ts +37 -0
- package/lib/generator.js +161 -0
- package/lib/tarballs/node.js +3 -3
- package/oclif.manifest.json +231 -58
- package/package.json +12 -9
- package/templates/src/command.ts.ejs +6 -6
- package/templates/src/hook.ts.ejs +1 -1
- package/lib/command-base.d.ts +0 -4
- package/lib/command-base.js +0 -12
- package/lib/generators/cli.d.ts +0 -41
- package/lib/generators/cli.js +0 -266
- package/lib/generators/command.d.ts +0 -11
- package/lib/generators/command.js +0 -76
- package/lib/generators/hook.d.ts +0 -15
- package/lib/generators/hook.js +0 -70
- package/lib/types/index.d.ts +0 -6
- package/lib/types/index.js +0 -2
package/README.md
CHANGED
|
@@ -1,54 +1,38 @@
|
|
|
1
1
|
<img src="https://user-images.githubusercontent.com/449385/38243295-e0a47d58-372e-11e8-9bc0-8c02a6f4d2ac.png" width="260" height="73">
|
|
2
2
|
|
|
3
|
-
# oclif
|
|
3
|
+
# `oclif` CLI
|
|
4
4
|
|
|
5
5
|
[](https://npmjs.org/package/oclif)
|
|
6
|
-
[](https://npmjs.org/package/oclif/oclif)
|
|
7
7
|
[](https://github.com/oclif/oclif/blob/main/package.json)
|
|
8
8
|
|
|
9
9
|
<!-- toc -->
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
10
|
+
|
|
11
|
+
- [`oclif` CLI](#oclif-cli)
|
|
12
|
+
- [π Description](#-description)
|
|
13
|
+
- [π Getting Started Tutorial](#-getting-started-tutorial)
|
|
14
|
+
- [π Requirements](#-requirements)
|
|
15
|
+
- [π Migrating from V1](#-migrating-from-v1)
|
|
16
|
+
- [π Usage](#-usage)
|
|
17
|
+
- [π Examples](#-examples)
|
|
18
|
+
- [π¨ Commands](#-commands)
|
|
19
|
+
- [Command Topics](#command-topics)
|
|
20
|
+
- [π Contributing](#-contributing)
|
|
21
|
+
- [π Related Repositories](#-related-repositories)
|
|
22
|
+
- [π¦ Learn More](#-learn-more)
|
|
23
23
|
<!-- tocstop -->
|
|
24
24
|
|
|
25
25
|
# π Description
|
|
26
26
|
|
|
27
|
-
This is
|
|
27
|
+
This is the `oclif` CLI for the [Open CLI Framework](https://github.com/oclif/core), that supports the development of oclif plugins and CLIs.
|
|
28
28
|
|
|
29
|
-
[See the docs for more information](http://oclif.io
|
|
29
|
+
[See the docs for more information](http://oclif.io).
|
|
30
30
|
|
|
31
31
|
# π Getting Started Tutorial
|
|
32
32
|
|
|
33
33
|
The [Getting Started tutorial](http://oclif.io/docs/introduction) 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.
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
- **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.
|
|
38
|
-
- **Super Speed** - The overhead for running an oclif CLI command is almost nothing. [It requires very few dependencies](https://www.npmjs.com/package/@oclif/command?activeTab=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 equally as fast as a small one with a single command.
|
|
39
|
-
- **CLI Generator** - Run a single command to scaffold out a fully functional CLI and get started quickly. See [Usage](#-usage) below.
|
|
40
|
-
- **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](https://github.com/oclif/hello-world/blob/main/test/commands/hello.test.ts).
|
|
41
|
-
- **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 [multi-command CLI example](https://github.com/oclif/example-multi-ts)
|
|
42
|
-
- **Plugins** - Using [plugins](https://oclif.io/docs/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](https://oclif.io/docs/plugins#building-your-own-plugin).
|
|
43
|
-
- **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.
|
|
44
|
-
- **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.
|
|
45
|
-
- **Auto-updating Installers** - oclif can package your CLI into [different installers](https://oclif.io/docs/releasing) that will not require the user to already have node installed on the machine. These can be made auto-updatable by using [plugin-update](https://github.com/oclif/plugin-update).
|
|
46
|
-
- **Everything is Customizable** - Pretty much anything can be swapped out and replaced inside oclif if neededβincluding the arg/flag parser.
|
|
47
|
-
- **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:
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
$ heroku info --app=<tab><tab> # will complete with all the Heroku apps a user has in their account
|
|
51
|
-
```
|
|
35
|
+
See [Usage](#-usage) below for an overview of the `oclif` CLI.
|
|
52
36
|
|
|
53
37
|
# π Requirements
|
|
54
38
|
|
|
@@ -111,29 +95,29 @@ hello world! (./src/commands/hello/world.ts)
|
|
|
111
95
|
# π¨ Commands
|
|
112
96
|
|
|
113
97
|
<!-- commands -->
|
|
98
|
+
|
|
114
99
|
# Command Topics
|
|
115
100
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
101
|
+
- [`oclif generate`](docs/generate.md) - Generate a new CLI
|
|
102
|
+
- [`oclif help`](docs/help.md) - Display help for oclif.
|
|
103
|
+
- [`oclif manifest`](docs/manifest.md) - Generates plugin manifest json (oclif.manifest.json).
|
|
104
|
+
- [`oclif pack`](docs/pack.md) - package an oclif CLI into installable artifacts
|
|
105
|
+
- [`oclif promote`](docs/promote.md) - Promote CLI builds to a S3 release channel.
|
|
106
|
+
- [`oclif readme`](docs/readme.md) - Adds commands to README.md in current directory.
|
|
107
|
+
- [`oclif upload`](docs/upload.md) - upload installable CLI artifacts to AWS S3
|
|
123
108
|
|
|
124
109
|
<!-- commandsstop -->
|
|
125
110
|
|
|
111
|
+
# π Contributing
|
|
112
|
+
|
|
113
|
+
See the [contributing guide](./CONRTIBUTING.md).
|
|
114
|
+
|
|
126
115
|
# π Related Repositories
|
|
127
116
|
|
|
128
117
|
- [@oclif/core](https://github.com/oclif/core) - Base library for oclif. This can be used directly without the generator.
|
|
129
|
-
- [@oclif/cli-ux](https://github.com/oclif/cli-ux) - Library for common CLI UI utilities.
|
|
130
118
|
- [@oclif/test](https://github.com/oclif/test) - Test helper for oclif.
|
|
131
119
|
|
|
132
120
|
# π¦ Learn More
|
|
133
121
|
|
|
134
122
|
- [Salesforce Release Announcement](https://engineering.salesforce.com/open-sourcing-oclif-the-cli-framework-that-powers-our-clis-21fbda99d33a)
|
|
135
123
|
- [Heroku Release Announcement](https://blog.heroku.com/open-cli-framework)
|
|
136
|
-
|
|
137
|
-
# π£ Feedback
|
|
138
|
-
|
|
139
|
-
If you have any suggestions or want to let us know what you think of oclif, send us a message at <alm-cli@salesforce.com>
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
export default class GenerateCommand extends
|
|
1
|
+
import { GeneratorCommand } from '../../generator';
|
|
2
|
+
export default class GenerateCommand extends GeneratorCommand<typeof GenerateCommand> {
|
|
3
3
|
static args: {
|
|
4
4
|
name: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
5
|
};
|
|
6
6
|
static description: string;
|
|
7
7
|
static flags: {
|
|
8
|
+
'commands-dir': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
9
|
force: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
10
|
};
|
|
10
11
|
run(): Promise<void>;
|
|
@@ -4,21 +4,44 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const core_1 = require("@oclif/core");
|
|
7
|
-
const
|
|
8
|
-
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const change_case_1 = require("change-case");
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
const generator_1 = require("../../generator");
|
|
11
|
+
class GenerateCommand extends generator_1.GeneratorCommand {
|
|
9
12
|
static args = {
|
|
10
13
|
name: core_1.Args.string({ description: 'name of command', required: true }),
|
|
11
14
|
};
|
|
12
|
-
static description = '
|
|
15
|
+
static description = 'Add a command to an existing CLI or plugin.';
|
|
13
16
|
static flags = {
|
|
14
|
-
|
|
17
|
+
'commands-dir': core_1.Flags.string({ default: 'src/commands', description: 'The directory to create the command in.' }),
|
|
18
|
+
force: core_1.Flags.boolean({ description: 'Overwrite existing files.' }),
|
|
15
19
|
};
|
|
16
20
|
async run() {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
});
|
|
21
|
+
const packageJSON = await (0, generator_1.readPJSON)(process.cwd());
|
|
22
|
+
if (!packageJSON)
|
|
23
|
+
throw new core_1.Errors.CLIError('not in a project directory');
|
|
24
|
+
const topicSeparator = packageJSON.oclif?.topicSeparator ?? ':';
|
|
25
|
+
this.log(`Adding ${chalk_1.default.dim(this.args.name.replaceAll(':', topicSeparator))} to ${packageJSON.name}!`);
|
|
26
|
+
const cmdPath = this.args.name.split(':').join('/');
|
|
27
|
+
const destination = (0, node_path_1.join)(process.cwd(), this.flags['commands-dir'], `${cmdPath}.ts`);
|
|
28
|
+
let bin = packageJSON.oclif?.bin ?? packageJSON.oclif?.dirname ?? packageJSON.name;
|
|
29
|
+
if (bin.includes('/'))
|
|
30
|
+
bin = bin.split('/').at(-1);
|
|
31
|
+
const opts = {
|
|
32
|
+
bin,
|
|
33
|
+
className: (0, change_case_1.pascalCase)(this.args.name),
|
|
34
|
+
cmd: `${bin} ${this.args.name}`,
|
|
35
|
+
name: this.args.name,
|
|
36
|
+
path: destination,
|
|
37
|
+
type: 'command',
|
|
38
|
+
};
|
|
39
|
+
await this.template((0, node_path_1.join)(this.templatesDir, 'src', 'command.ts.ejs'), destination, opts);
|
|
40
|
+
if (packageJSON.devDependencies?.mocha) {
|
|
41
|
+
const testTemplatePath = (0, node_path_1.join)(this.templatesDir, 'test', 'command.test.ts.ejs');
|
|
42
|
+
const testDestination = (0, node_path_1.join)(process.cwd(), 'test', 'commands', `${cmdPath}.test.ts`);
|
|
43
|
+
await this.template(testTemplatePath, testDestination, opts);
|
|
44
|
+
}
|
|
22
45
|
}
|
|
23
46
|
}
|
|
24
47
|
exports.default = GenerateCommand;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
export default class GenerateHook extends
|
|
1
|
+
import { GeneratorCommand } from '../../generator';
|
|
2
|
+
export default class GenerateHook extends GeneratorCommand<typeof GenerateHook> {
|
|
3
3
|
static args: {
|
|
4
4
|
name: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
5
|
};
|
|
@@ -27,28 +27,57 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
29
|
const core_1 = require("@oclif/core");
|
|
30
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
30
31
|
const fs = __importStar(require("fs-extra"));
|
|
32
|
+
const promises_1 = require("node:fs/promises");
|
|
31
33
|
const node_path_1 = require("node:path");
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
+
const generator_1 = require("../../generator");
|
|
35
|
+
const util_1 = require("../../util");
|
|
36
|
+
class GenerateHook extends generator_1.GeneratorCommand {
|
|
34
37
|
static args = {
|
|
35
|
-
name: core_1.Args.string({ description: '
|
|
38
|
+
name: core_1.Args.string({ description: 'Name of hook (snake_case).', required: true }),
|
|
36
39
|
};
|
|
37
|
-
static description = '
|
|
40
|
+
static description = 'Add a hook to an existing CLI or plugin.';
|
|
38
41
|
static flags = {
|
|
39
|
-
event: core_1.Flags.string({
|
|
40
|
-
|
|
42
|
+
event: core_1.Flags.string({
|
|
43
|
+
default: 'init',
|
|
44
|
+
description: 'Event to run hook on.',
|
|
45
|
+
}),
|
|
46
|
+
force: core_1.Flags.boolean({
|
|
47
|
+
description: 'Overwrite existing files.',
|
|
48
|
+
}),
|
|
41
49
|
};
|
|
42
50
|
async run() {
|
|
43
|
-
const
|
|
51
|
+
const packageJSON = await (0, generator_1.readPJSON)(process.cwd());
|
|
52
|
+
if (!packageJSON)
|
|
53
|
+
throw new core_1.Errors.CLIError('not in a project directory');
|
|
54
|
+
this.log(`Adding a ${chalk_1.default.dim(this.flags.event)} hook to ${packageJSON.name}!`);
|
|
55
|
+
const source = (0, node_path_1.join)(this.templatesDir, 'src', 'hook.ts.ejs');
|
|
56
|
+
const dest = (0, node_path_1.join)(process.cwd(), 'src', 'hooks', this.flags.event, `${this.args.name}.ts`);
|
|
57
|
+
await this.template(source, dest, { event: this.flags.event });
|
|
58
|
+
if (packageJSON.devDependencies?.mocha) {
|
|
59
|
+
const testSource = (0, node_path_1.join)(this.templatesDir, 'test', 'hook.test.ts.ejs');
|
|
60
|
+
const testDest = (0, node_path_1.join)(process.cwd(), 'test', 'hooks', this.flags.event, `${this.args.name}.test.ts`);
|
|
61
|
+
await this.template(testSource, testDest);
|
|
62
|
+
}
|
|
44
63
|
const tsConfigPath = (0, node_path_1.resolve)(process.cwd(), 'tsconfig.json');
|
|
45
64
|
const tsConfig = await fs.readJSON(tsConfigPath).catch(() => ({}));
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
65
|
+
const outDir = tsConfig.compilerOptions?.outDir ?? 'dist';
|
|
66
|
+
const hooks = packageJSON.oclif?.hooks ?? {};
|
|
67
|
+
hooks[this.flags.event] = hooks[this.flags.event]
|
|
68
|
+
? (0, util_1.uniq)([
|
|
69
|
+
...(0, util_1.castArray)(hooks[this.flags.event]),
|
|
70
|
+
`./${outDir}/hooks/${this.flags.event}/${this.args.name}`,
|
|
71
|
+
]).sort()
|
|
72
|
+
: `./${outDir}/hooks/${this.flags.event}/${this.args.name}`;
|
|
73
|
+
const updatedPackageJSON = {
|
|
74
|
+
...packageJSON,
|
|
75
|
+
oclif: {
|
|
76
|
+
...packageJSON.oclif,
|
|
77
|
+
hooks,
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
await (0, promises_1.writeFile)((0, node_path_1.resolve)(process.cwd(), 'package.json'), JSON.stringify(updatedPackageJSON, null, 2));
|
|
52
81
|
}
|
|
53
82
|
}
|
|
54
83
|
exports.default = GenerateHook;
|
|
@@ -1,11 +1,65 @@
|
|
|
1
|
-
import
|
|
2
|
-
export default class Generate extends
|
|
1
|
+
import { GeneratorCommand } from '../generator';
|
|
2
|
+
export default class Generate extends GeneratorCommand<typeof Generate> {
|
|
3
3
|
static args: {
|
|
4
4
|
name: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
5
|
};
|
|
6
6
|
static description: string;
|
|
7
|
+
static examples: {
|
|
8
|
+
command: string;
|
|
9
|
+
description: string;
|
|
10
|
+
}[];
|
|
11
|
+
static flaggablePrompts: {
|
|
12
|
+
author: {
|
|
13
|
+
message: string;
|
|
14
|
+
validate: (d: string) => true | "Author cannot be empty";
|
|
15
|
+
};
|
|
16
|
+
bin: {
|
|
17
|
+
message: string;
|
|
18
|
+
validate: (d: string) => true | "Invalid bin name";
|
|
19
|
+
};
|
|
20
|
+
description: {
|
|
21
|
+
message: string;
|
|
22
|
+
validate: (d: string) => true | "Description cannot be empty";
|
|
23
|
+
};
|
|
24
|
+
license: {
|
|
25
|
+
message: string;
|
|
26
|
+
validate: (d: string) => true | "License cannot be empty";
|
|
27
|
+
};
|
|
28
|
+
'module-type': {
|
|
29
|
+
message: string;
|
|
30
|
+
options: string[];
|
|
31
|
+
validate: (d: string) => true | "Invalid module type";
|
|
32
|
+
};
|
|
33
|
+
name: {
|
|
34
|
+
message: string;
|
|
35
|
+
validate: (d: string) => true | "Invalid package name";
|
|
36
|
+
};
|
|
37
|
+
owner: {
|
|
38
|
+
message: string;
|
|
39
|
+
validate: (d: string) => true | "Owner cannot be empty";
|
|
40
|
+
};
|
|
41
|
+
'package-manager': {
|
|
42
|
+
message: string;
|
|
43
|
+
options: string[];
|
|
44
|
+
validate: (d: string) => true | "Invalid package manager";
|
|
45
|
+
};
|
|
46
|
+
repository: {
|
|
47
|
+
message: string;
|
|
48
|
+
validate: (d: string) => true | "Repo cannot be empty";
|
|
49
|
+
};
|
|
50
|
+
};
|
|
7
51
|
static flags: {
|
|
8
|
-
|
|
52
|
+
'output-dir': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
53
|
+
name: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
54
|
+
description: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
55
|
+
bin: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
56
|
+
author: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
57
|
+
license: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
58
|
+
"module-type": import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
59
|
+
owner: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
60
|
+
"package-manager": import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
61
|
+
repository: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
62
|
};
|
|
63
|
+
static summary: string;
|
|
10
64
|
run(): Promise<void>;
|
|
11
65
|
}
|
package/lib/commands/generate.js
CHANGED
|
@@ -3,24 +3,237 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
/* eslint-disable unicorn/no-await-expression-member */
|
|
6
7
|
const core_1 = require("@oclif/core");
|
|
7
|
-
const
|
|
8
|
-
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const promises_1 = require("node:fs/promises");
|
|
10
|
+
const node_path_1 = require("node:path");
|
|
11
|
+
const validate_npm_package_name_1 = __importDefault(require("validate-npm-package-name"));
|
|
12
|
+
const generator_1 = require("../generator");
|
|
13
|
+
const util_1 = require("../util");
|
|
14
|
+
async function fetchGithubUserFromAPI() {
|
|
15
|
+
const token = process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN;
|
|
16
|
+
if (!token)
|
|
17
|
+
return;
|
|
18
|
+
const { default: got } = await import('got');
|
|
19
|
+
const headers = {
|
|
20
|
+
Accept: 'application/vnd.github.v3+json',
|
|
21
|
+
Authorization: `Bearer ${token}`,
|
|
22
|
+
};
|
|
23
|
+
try {
|
|
24
|
+
const { login, name } = await got('https://api.github.com/user', { headers }).json();
|
|
25
|
+
return { login, name };
|
|
26
|
+
}
|
|
27
|
+
catch { }
|
|
28
|
+
}
|
|
29
|
+
async function fetchGithubUserFromGit() {
|
|
30
|
+
try {
|
|
31
|
+
const result = await (0, generator_1.exec)('git config --get user.name');
|
|
32
|
+
return result.stdout.trim();
|
|
33
|
+
}
|
|
34
|
+
catch { }
|
|
35
|
+
}
|
|
36
|
+
async function fetchGithubUser() {
|
|
37
|
+
return (await fetchGithubUserFromAPI()) ?? { name: await fetchGithubUserFromGit() };
|
|
38
|
+
}
|
|
39
|
+
function determineDefaultAuthor(user, defaultValue) {
|
|
40
|
+
const { login, name } = user ?? { login: undefined, name: undefined };
|
|
41
|
+
if (name && login)
|
|
42
|
+
return `${name} @${login}`;
|
|
43
|
+
if (name)
|
|
44
|
+
return name;
|
|
45
|
+
if (login)
|
|
46
|
+
return `@${login}`;
|
|
47
|
+
return defaultValue;
|
|
48
|
+
}
|
|
49
|
+
async function clone(repo, location) {
|
|
50
|
+
try {
|
|
51
|
+
await (0, generator_1.exec)(`git clone https://github.com/oclif/${repo}.git "${location}" --depth=1`);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
const err = error instanceof Error
|
|
55
|
+
? new core_1.Errors.CLIError(error)
|
|
56
|
+
: new core_1.Errors.CLIError('An error occurred while cloning the template repo');
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const FLAGGABLE_PROMPTS = {
|
|
61
|
+
author: {
|
|
62
|
+
message: 'Author',
|
|
63
|
+
validate: (d) => d.length > 0 || 'Author cannot be empty',
|
|
64
|
+
},
|
|
65
|
+
bin: {
|
|
66
|
+
message: 'Command bin name the CLI will export',
|
|
67
|
+
validate: (d) => (0, util_1.validateBin)(d) || 'Invalid bin name',
|
|
68
|
+
},
|
|
69
|
+
description: {
|
|
70
|
+
message: 'Description',
|
|
71
|
+
validate: (d) => d.length > 0 || 'Description cannot be empty',
|
|
72
|
+
},
|
|
73
|
+
license: {
|
|
74
|
+
message: 'License',
|
|
75
|
+
validate: (d) => d.length > 0 || 'License cannot be empty',
|
|
76
|
+
},
|
|
77
|
+
'module-type': {
|
|
78
|
+
message: 'Select a module type',
|
|
79
|
+
options: ['CommonJS', 'ESM'],
|
|
80
|
+
validate: (d) => ['CommonJS', 'ESM'].includes(d) || 'Invalid module type',
|
|
81
|
+
},
|
|
82
|
+
name: {
|
|
83
|
+
message: 'NPM package name',
|
|
84
|
+
validate: (d) => (0, validate_npm_package_name_1.default)(d).validForNewPackages || 'Invalid package name',
|
|
85
|
+
},
|
|
86
|
+
owner: {
|
|
87
|
+
message: 'Who is the GitHub owner of repository (https://github.com/OWNER/repo)',
|
|
88
|
+
validate: (d) => d.length > 0 || 'Owner cannot be empty',
|
|
89
|
+
},
|
|
90
|
+
'package-manager': {
|
|
91
|
+
message: 'Select a package manager',
|
|
92
|
+
options: ['npm', 'yarn'],
|
|
93
|
+
validate: (d) => ['npm', 'yarn'].includes(d) || 'Invalid package manager',
|
|
94
|
+
},
|
|
95
|
+
repository: {
|
|
96
|
+
message: 'What is the GitHub name of repository (https://github.com/owner/REPO)',
|
|
97
|
+
validate: (d) => d.length > 0 || 'Repo cannot be empty',
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
class Generate extends generator_1.GeneratorCommand {
|
|
9
101
|
static args = {
|
|
10
|
-
name: core_1.Args.string({ description: '
|
|
102
|
+
name: core_1.Args.string({ description: 'Directory name of new project.', required: true }),
|
|
11
103
|
};
|
|
12
|
-
static description = `
|
|
13
|
-
|
|
104
|
+
static description = `This will clone the template repo and update package properties. For CommonJS, the 'oclif/hello-world' template will be used and for ESM, the 'oclif/hello-world-esm' template will be used.`;
|
|
105
|
+
static examples = [
|
|
106
|
+
{
|
|
107
|
+
command: '<%= config.bin %> <%= command.id %> my-cli',
|
|
108
|
+
description: 'Generate a new CLI with prompts for all properties',
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
command: '<%= config.bin %> <%= command.id %> my-cli --yes',
|
|
112
|
+
description: 'Automatically accept default values for all prompts',
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
command: '<%= config.bin %> <%= command.id %> my-cli --module-type CommonJS --author "John Doe"',
|
|
116
|
+
description: 'Supply answers for specific prompts',
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
command: '<%= config.bin %> <%= command.id %> my-cli --module-type CommonJS --author "John Doe" --yes',
|
|
120
|
+
description: 'Supply answers for specific prompts and accept default values for the rest',
|
|
121
|
+
},
|
|
122
|
+
];
|
|
123
|
+
static flaggablePrompts = FLAGGABLE_PROMPTS;
|
|
14
124
|
static flags = {
|
|
15
|
-
|
|
125
|
+
...(0, generator_1.makeFlags)(FLAGGABLE_PROMPTS),
|
|
126
|
+
'output-dir': core_1.Flags.directory({
|
|
127
|
+
char: 'd',
|
|
128
|
+
description: 'Directory to build the CLI in.',
|
|
129
|
+
}),
|
|
16
130
|
};
|
|
131
|
+
static summary = 'Generate a new CLI';
|
|
17
132
|
async run() {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
name:
|
|
133
|
+
const location = this.flags['output-dir'] ? (0, node_path_1.join)(this.flags['output-dir'], this.args.name) : (0, node_path_1.resolve)(this.args.name);
|
|
134
|
+
this.log(`Generating ${this.args.name} in ${chalk_1.default.green(location)}`);
|
|
135
|
+
const moduleType = await this.getFlagOrPrompt({
|
|
136
|
+
defaultValue: 'ESM',
|
|
137
|
+
name: 'module-type',
|
|
138
|
+
type: 'select',
|
|
139
|
+
});
|
|
140
|
+
const template = moduleType === 'ESM' ? 'hello-world-esm' : 'hello-world';
|
|
141
|
+
await clone(template, location);
|
|
142
|
+
await (0, promises_1.rm)((0, node_path_1.join)(location, '.git'), { force: true, recursive: true });
|
|
143
|
+
// We just cloned the template repo so we're sure it has a package.json
|
|
144
|
+
const packageJSON = (await (0, generator_1.readPJSON)(location));
|
|
145
|
+
const githubUser = await fetchGithubUser();
|
|
146
|
+
const name = await this.getFlagOrPrompt({ defaultValue: this.args.name, name: 'name', type: 'input' });
|
|
147
|
+
const bin = await this.getFlagOrPrompt({ defaultValue: name, name: 'bin', type: 'input' });
|
|
148
|
+
const description = await this.getFlagOrPrompt({
|
|
149
|
+
defaultValue: packageJSON.description,
|
|
150
|
+
name: 'description',
|
|
151
|
+
type: 'input',
|
|
152
|
+
});
|
|
153
|
+
const author = await this.getFlagOrPrompt({
|
|
154
|
+
defaultValue: determineDefaultAuthor(githubUser, packageJSON.author),
|
|
155
|
+
name: 'author',
|
|
156
|
+
type: 'input',
|
|
157
|
+
});
|
|
158
|
+
const license = await this.getFlagOrPrompt({
|
|
159
|
+
defaultValue: packageJSON.license,
|
|
160
|
+
name: 'license',
|
|
161
|
+
type: 'input',
|
|
162
|
+
});
|
|
163
|
+
const owner = await this.getFlagOrPrompt({
|
|
164
|
+
defaultValue: githubUser?.login ?? location.split(node_path_1.sep).at(-2) ?? packageJSON.author,
|
|
165
|
+
name: 'owner',
|
|
166
|
+
type: 'input',
|
|
167
|
+
});
|
|
168
|
+
const repository = await this.getFlagOrPrompt({
|
|
169
|
+
defaultValue: (name ?? packageJSON.repository ?? packageJSON.name).split('/').at(-1) ?? name,
|
|
170
|
+
name: 'repository',
|
|
171
|
+
type: 'input',
|
|
172
|
+
});
|
|
173
|
+
const packageManager = await this.getFlagOrPrompt({
|
|
174
|
+
defaultValue: 'npm',
|
|
175
|
+
name: 'package-manager',
|
|
176
|
+
type: 'select',
|
|
177
|
+
});
|
|
178
|
+
const updatedPackageJSON = {
|
|
179
|
+
...packageJSON,
|
|
180
|
+
author,
|
|
181
|
+
bin: { [bin]: './bin/run.js' },
|
|
182
|
+
bugs: `https://github.com/${owner}/${repository}/issues`,
|
|
183
|
+
description,
|
|
184
|
+
homepage: `https://github.com/${owner}/${repository}`,
|
|
185
|
+
license,
|
|
186
|
+
name,
|
|
187
|
+
oclif: {
|
|
188
|
+
...packageJSON.oclif,
|
|
189
|
+
bin,
|
|
190
|
+
dirname: bin,
|
|
191
|
+
},
|
|
192
|
+
repository: `${owner}/${repository}`,
|
|
193
|
+
};
|
|
194
|
+
if (packageManager === 'npm') {
|
|
195
|
+
const scripts = (updatedPackageJSON.scripts || {});
|
|
196
|
+
updatedPackageJSON.scripts = Object.fromEntries(Object.entries(scripts).map(([k, v]) => [k, v.replace('yarn', 'npm run')]));
|
|
197
|
+
}
|
|
198
|
+
const { default: sortPackageJson } = await import('sort-package-json');
|
|
199
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(location, 'package.json'), JSON.stringify(sortPackageJson(updatedPackageJSON), null, 2));
|
|
200
|
+
await (0, promises_1.rm)((0, node_path_1.join)(location, 'LICENSE'));
|
|
201
|
+
const existing = (await (0, promises_1.readFile)((0, node_path_1.join)(location, '.gitignore'), 'utf8')).split('\n');
|
|
202
|
+
const updated = (0, util_1.uniq)((0, util_1.compact)([
|
|
203
|
+
'*-debug.log',
|
|
204
|
+
'*-error.log',
|
|
205
|
+
'node_modules',
|
|
206
|
+
'/tmp',
|
|
207
|
+
'/dist',
|
|
208
|
+
'/lib',
|
|
209
|
+
...(packageManager === 'yarn'
|
|
210
|
+
? [
|
|
211
|
+
'/package-lock.json',
|
|
212
|
+
'.pnp.*',
|
|
213
|
+
'.yarn/*',
|
|
214
|
+
'!.yarn/patches',
|
|
215
|
+
'!.yarn/plugins',
|
|
216
|
+
'!.yarn/releases',
|
|
217
|
+
'!.yarn/sdks',
|
|
218
|
+
'!.yarn/versions',
|
|
219
|
+
]
|
|
220
|
+
: ['/yarn.lock']),
|
|
221
|
+
...existing,
|
|
222
|
+
]))
|
|
223
|
+
.sort()
|
|
224
|
+
.join('\n') + '\n';
|
|
225
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(location, '.gitignore'), updated);
|
|
226
|
+
await (0, generator_1.exec)(`${packageManager} install`, { cwd: location, silent: false });
|
|
227
|
+
await (0, generator_1.exec)(`${(0, node_path_1.join)(location, 'node_modules', '.bin', 'oclif')} readme`, {
|
|
228
|
+
cwd: location,
|
|
229
|
+
// When testing this command in development, you get noisy compilation errors as a result of running
|
|
230
|
+
// this in a spawned process. Setting the NODE_ENV to production will silence these warnings. This
|
|
231
|
+
// doesn't affect the behavior of the command in production since the NODE_ENV is already set to production
|
|
232
|
+
// in that scenario.
|
|
233
|
+
env: { ...process.env, NODE_ENV: 'production' },
|
|
234
|
+
silent: false,
|
|
23
235
|
});
|
|
236
|
+
this.log(`\nCreated ${chalk_1.default.green(name)}`);
|
|
24
237
|
}
|
|
25
238
|
}
|
|
26
239
|
exports.default = Generate;
|
package/lib/commands/manifest.js
CHANGED
|
@@ -22,13 +22,9 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
22
22
|
__setModuleDefault(result, mod);
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
-
};
|
|
28
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
26
|
const core_1 = require("@oclif/core");
|
|
30
27
|
const fs_extra_1 = require("fs-extra");
|
|
31
|
-
const got_1 = __importDefault(require("got"));
|
|
32
28
|
const node_child_process_1 = require("node:child_process");
|
|
33
29
|
const os = __importStar(require("node:os"));
|
|
34
30
|
const path = __importStar(require("node:path"));
|
|
@@ -47,14 +43,14 @@ async function fileExists(filePath) {
|
|
|
47
43
|
}
|
|
48
44
|
class Manifest extends core_1.Command {
|
|
49
45
|
static args = {
|
|
50
|
-
path: core_1.Args.string({ default: '.', description: '
|
|
46
|
+
path: core_1.Args.string({ default: '.', description: 'Path to plugin.' }),
|
|
51
47
|
};
|
|
52
|
-
static description = '
|
|
48
|
+
static description = 'Generates plugin manifest json (oclif.manifest.json).';
|
|
53
49
|
static flags = {
|
|
54
50
|
jit: core_1.Flags.boolean({
|
|
55
51
|
allowNo: true,
|
|
56
52
|
default: true,
|
|
57
|
-
summary: '
|
|
53
|
+
summary: 'Append commands from JIT plugins in manifest.',
|
|
58
54
|
}),
|
|
59
55
|
};
|
|
60
56
|
async run() {
|
|
@@ -70,6 +66,7 @@ class Manifest extends core_1.Command {
|
|
|
70
66
|
if (flags.jit && packageJson.oclif?.jitPlugins) {
|
|
71
67
|
this.debug('jitPlugins: %s', packageJson.oclif.jitPlugins);
|
|
72
68
|
const tmpDir = os.tmpdir();
|
|
69
|
+
const { default: got } = await import('got');
|
|
73
70
|
const promises = Object.entries(packageJson.oclif.jitPlugins).map(async ([jitPlugin, version]) => {
|
|
74
71
|
const pluginDir = jitPlugin.replace('/', '-').replace('@', '');
|
|
75
72
|
const fullPath = path.join(tmpDir, pluginDir);
|
|
@@ -79,7 +76,7 @@ class Manifest extends core_1.Command {
|
|
|
79
76
|
const resolvedVersion = await this.getVersion(jitPlugin, version);
|
|
80
77
|
const tarballUrl = await this.getTarballUrl(jitPlugin, resolvedVersion);
|
|
81
78
|
const tarball = path.join(fullPath, path.basename(tarballUrl));
|
|
82
|
-
await pipeline(
|
|
79
|
+
await pipeline(got.stream(tarballUrl), (0, fs_extra_1.createWriteStream)(tarball));
|
|
83
80
|
await this.executeCommand(`tar -xzf "${tarball}"`, { cwd: fullPath });
|
|
84
81
|
const manifest = (await (0, fs_extra_1.readJSON)(path.join(fullPath, 'package', 'oclif.manifest.json')));
|
|
85
82
|
for (const command of Object.values(manifest.commands)) {
|