cli-forge 0.5.0 → 0.7.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/bin/cli.js +3 -1
- package/bin/cli.js.map +1 -1
- package/bin/commands/generate-documentation.d.ts +3 -1
- package/bin/commands/generate-documentation.js +95 -11
- package/bin/commands/generate-documentation.js.map +1 -1
- package/bin/commands/init.d.ts +4 -0
- package/bin/commands/init.js +37 -3
- package/bin/commands/init.js.map +1 -1
- package/package.json +10 -4
- package/src/index.d.ts +2 -1
- package/src/index.js +5 -4
- package/src/index.js.map +1 -1
- package/src/lib/cli-forge.d.ts +33 -137
- package/src/lib/cli-forge.js +267 -71
- package/src/lib/cli-forge.js.map +1 -1
- package/src/lib/documentation.d.ts +16 -17
- package/src/lib/documentation.js +18 -2
- package/src/lib/documentation.js.map +1 -1
- package/src/lib/interactive-shell.d.ts +13 -0
- package/src/lib/interactive-shell.js +88 -0
- package/src/lib/interactive-shell.js.map +1 -0
- package/src/lib/public-api.d.ts +212 -0
- package/src/lib/public-api.js +15 -0
- package/src/lib/public-api.js.map +1 -0
- package/src/lib/test-harness.d.ts +1 -1
- package/src/lib/test-harness.js +3 -1
- package/src/lib/test-harness.js.map +1 -1
- package/src/lib/utils.d.ts +10 -0
- package/src/lib/utils.js +110 -0
- package/src/lib/utils.js.map +1 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { InternalCLI } from './cli-forge';
|
|
2
|
+
export interface InteractiveShellOptions {
|
|
3
|
+
prompt?: string;
|
|
4
|
+
prependArgs?: string[];
|
|
5
|
+
}
|
|
6
|
+
export declare let INTERACTIVE_SHELL: InteractiveShell | undefined;
|
|
7
|
+
export declare class InteractiveShell {
|
|
8
|
+
private readonly rl;
|
|
9
|
+
private listeners;
|
|
10
|
+
constructor(cli: InternalCLI<any>, opts?: InteractiveShellOptions);
|
|
11
|
+
registerLineListener(callback: (line: string) => Promise<void>): void;
|
|
12
|
+
close(): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InteractiveShell = exports.INTERACTIVE_SHELL = void 0;
|
|
4
|
+
const readline = require("readline");
|
|
5
|
+
const utils_1 = require("./utils");
|
|
6
|
+
const child_process_1 = require("child_process");
|
|
7
|
+
const parser_1 = require("@cli-forge/parser");
|
|
8
|
+
function normalizeShellOptions(cli, options) {
|
|
9
|
+
return {
|
|
10
|
+
prompt: options?.prompt ??
|
|
11
|
+
(() => {
|
|
12
|
+
const chain = [cli.name];
|
|
13
|
+
if (cli.commandChain.length > 2) {
|
|
14
|
+
chain.push('...', ...cli.commandChain[cli.command.length - 1]);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
chain.push(...cli.commandChain);
|
|
18
|
+
}
|
|
19
|
+
return chain.join(' ') + '> ';
|
|
20
|
+
})(),
|
|
21
|
+
prependArgs: options?.prependArgs ?? [],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
class InteractiveShell {
|
|
25
|
+
rl;
|
|
26
|
+
listeners = [];
|
|
27
|
+
constructor(cli, opts) {
|
|
28
|
+
if (exports.INTERACTIVE_SHELL) {
|
|
29
|
+
throw new Error('Only one interactive shell can be created at a time. Make sure the other instance is closed.');
|
|
30
|
+
}
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
32
|
+
exports.INTERACTIVE_SHELL = this;
|
|
33
|
+
const { prompt, prependArgs } = normalizeShellOptions(cli, opts);
|
|
34
|
+
this.rl = readline
|
|
35
|
+
.createInterface({
|
|
36
|
+
input: process.stdin,
|
|
37
|
+
output: process.stdout,
|
|
38
|
+
prompt: prompt,
|
|
39
|
+
})
|
|
40
|
+
.on('SIGINT', () => {
|
|
41
|
+
process.emit('SIGINT');
|
|
42
|
+
});
|
|
43
|
+
this.rl.prompt();
|
|
44
|
+
this.registerLineListener(async (line) => {
|
|
45
|
+
const nextArgs = (0, utils_1.stringToArgs)(line);
|
|
46
|
+
let currentCommand = cli;
|
|
47
|
+
for (const subcommand of cli.commandChain) {
|
|
48
|
+
currentCommand = currentCommand.registeredCommands[subcommand];
|
|
49
|
+
}
|
|
50
|
+
if (currentCommand.registeredCommands[nextArgs[0]]) {
|
|
51
|
+
(0, child_process_1.spawnSync)(process.execPath, [
|
|
52
|
+
...process.execArgv,
|
|
53
|
+
(0, parser_1.getBin)(process.argv),
|
|
54
|
+
...prependArgs,
|
|
55
|
+
...nextArgs,
|
|
56
|
+
], { stdio: 'inherit' });
|
|
57
|
+
}
|
|
58
|
+
else if (line.trim()) {
|
|
59
|
+
try {
|
|
60
|
+
(0, child_process_1.execSync)(line, { stdio: 'inherit' });
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// ignore
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
registerLineListener(callback) {
|
|
69
|
+
const wrapped = async (line) => {
|
|
70
|
+
this.rl.pause();
|
|
71
|
+
await callback(line);
|
|
72
|
+
this.rl.prompt();
|
|
73
|
+
};
|
|
74
|
+
this.listeners.push(wrapped);
|
|
75
|
+
this.rl.on('line', wrapped);
|
|
76
|
+
}
|
|
77
|
+
close() {
|
|
78
|
+
this.listeners.forEach((listener) => this.rl.off('line', listener));
|
|
79
|
+
this.rl.close();
|
|
80
|
+
readline.moveCursor(process.stdout, -1 * this.rl.getCursorPos().cols, 0);
|
|
81
|
+
readline.clearScreenDown(process.stdout);
|
|
82
|
+
if (exports.INTERACTIVE_SHELL === this) {
|
|
83
|
+
exports.INTERACTIVE_SHELL = undefined;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
exports.InteractiveShell = InteractiveShell;
|
|
88
|
+
//# sourceMappingURL=interactive-shell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interactive-shell.js","sourceRoot":"","sources":["../../../../../packages/cli-forge/src/lib/interactive-shell.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { ArrayOptionConfig, OptionConfig, ParsedArgs } from '@cli-forge/parser';
|
|
2
|
+
/**
|
|
3
|
+
* The interface for a CLI application or subcommands.
|
|
4
|
+
*
|
|
5
|
+
* {@link cli} is provided as a small helper function to create a new CLI instance.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { cli } from 'cli-forge';
|
|
10
|
+
*
|
|
11
|
+
* cli('basic-cli').command('hello', {
|
|
12
|
+
* builder: (args) =>
|
|
13
|
+
* args.option('name', {
|
|
14
|
+
* type: 'string',
|
|
15
|
+
* }),
|
|
16
|
+
* handler: (args) => {
|
|
17
|
+
* console.log(`Hello, ${args.name}!`);
|
|
18
|
+
* }).forge();
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export interface CLI<TArgs extends ParsedArgs = ParsedArgs> {
|
|
22
|
+
command<TCommandArgs extends TArgs>(cmd: Command<TArgs, TCommandArgs>): CLI<TArgs>;
|
|
23
|
+
/**
|
|
24
|
+
* Registers a new command with the CLI.
|
|
25
|
+
* @param key What should the new command be called?
|
|
26
|
+
* @param options Settings for the new command. See {@link CLICommandOptions}.
|
|
27
|
+
* @returns Updated CLI instance with the new command registered.
|
|
28
|
+
*/
|
|
29
|
+
command<TCommandArgs extends TArgs>(key: string, options: CLICommandOptions<TArgs, TCommandArgs>): CLI<TArgs>;
|
|
30
|
+
/**
|
|
31
|
+
* Registers multiple subcommands with the CLI.
|
|
32
|
+
* @param commands Several commands to register. Can be the result of a call to {@link cli} or a configuration object.
|
|
33
|
+
*/
|
|
34
|
+
commands(commands: Command[]): CLI<TArgs>;
|
|
35
|
+
/**
|
|
36
|
+
* Registers multiple subcommands with the CLI.
|
|
37
|
+
* @param commands Several commands to register. Can be the result of a call to {@link cli} or a configuration object.
|
|
38
|
+
*/
|
|
39
|
+
commands(...commands: Command[]): CLI<TArgs>;
|
|
40
|
+
/**
|
|
41
|
+
* Enables the ability to run CLI commands that contain subcommands as an interactive shell.
|
|
42
|
+
* This presents as a small shell that only knows the current command and its subcommands.
|
|
43
|
+
* Any flags already consumed by the command will be passed to every subcommand invocation.
|
|
44
|
+
*/
|
|
45
|
+
enableInteractiveShell(): CLI<TArgs>;
|
|
46
|
+
/**
|
|
47
|
+
* Registers a custom global error handler for the CLI. This handler will be called when an error is thrown
|
|
48
|
+
* during the execution of the CLI and not otherwise handled. Error handlers should re-throw the error if they
|
|
49
|
+
* cannot handle it, s.t. the next error handler can attempt to handle it.
|
|
50
|
+
*
|
|
51
|
+
* @param handler Typically called with an Error object, but you should be prepared to handle any type of error.
|
|
52
|
+
* @param actions Actions that can be taken by the error handler. Prefer using these over process.exit for better support of interactive shells.
|
|
53
|
+
*/
|
|
54
|
+
errorHandler(handler: ErrorHandler): CLI<TArgs>;
|
|
55
|
+
/**
|
|
56
|
+
* Registers a new option for the CLI command. This option will be accessible
|
|
57
|
+
* within the command handler, as well as any subcommands.
|
|
58
|
+
*
|
|
59
|
+
* @param name The name of the option.
|
|
60
|
+
* @param config Configuration for the option. See {@link OptionConfig}.
|
|
61
|
+
* @returns Updated CLI instance with the new option registered.
|
|
62
|
+
*/
|
|
63
|
+
option<TOption extends string, TOptionConfig extends OptionConfig>(name: TOption, config: TOptionConfig): CLI<TArgs & {
|
|
64
|
+
[key in TOption]: TOptionConfig['coerce'] extends (value: string) => infer TCoerce ? TCoerce : {
|
|
65
|
+
string: string;
|
|
66
|
+
number: number;
|
|
67
|
+
boolean: boolean;
|
|
68
|
+
array: (TOptionConfig extends ArrayOptionConfig<string | number> ? TOptionConfig['items'] extends 'string' ? string : number : never)[];
|
|
69
|
+
}[TOptionConfig['type']];
|
|
70
|
+
}>;
|
|
71
|
+
/**
|
|
72
|
+
* Registers a new positional argument for the CLI command. This argument will be accessible
|
|
73
|
+
* within the command handler, as well as any subcommands.
|
|
74
|
+
* @param name The name of the positional argument.
|
|
75
|
+
* @param config Configuration for the positional argument. See {@link OptionConfig}.
|
|
76
|
+
* @returns Updated CLI instance with the new positional argument registered.
|
|
77
|
+
*/
|
|
78
|
+
positional<TOption extends string, TOptionConfig extends OptionConfig>(name: TOption, config: TOptionConfig): CLI<TArgs & {
|
|
79
|
+
[key in TOption]: TOptionConfig['coerce'] extends (value: string) => infer TCoerce ? TCoerce : {
|
|
80
|
+
string: string;
|
|
81
|
+
number: number;
|
|
82
|
+
boolean: boolean;
|
|
83
|
+
array: (TOptionConfig extends ArrayOptionConfig<string | number> ? TOptionConfig['items'] extends 'string' ? string : number : never)[];
|
|
84
|
+
}[TOptionConfig['type']];
|
|
85
|
+
}>;
|
|
86
|
+
/**
|
|
87
|
+
* Adds support for reading CLI options from environment variables.
|
|
88
|
+
* @param prefix The prefix to use when looking up environment variables. Defaults to the command name.
|
|
89
|
+
*/
|
|
90
|
+
env(prefix?: string): CLI<TArgs>;
|
|
91
|
+
/**
|
|
92
|
+
* Sets a group of options as mutually exclusive. If more than one option is provided, there will be a validation error.
|
|
93
|
+
* @param options The options that should be mutually exclusive.
|
|
94
|
+
*/
|
|
95
|
+
conflicts(...options: [string, string, ...string[]]): CLI<TArgs>;
|
|
96
|
+
/**
|
|
97
|
+
* Sets a group of options as mutually inclusive. If one option is provided, all other options must also be provided.
|
|
98
|
+
* @param option The option that implies the other options.
|
|
99
|
+
* @param impliedOptions The options which become required when the option is provided.
|
|
100
|
+
*/
|
|
101
|
+
implies(option: string, ...impliedOptions: string[]): CLI<TArgs>;
|
|
102
|
+
/**
|
|
103
|
+
* Requires a command to be provided when executing the CLI. Useful if your parent command
|
|
104
|
+
* cannot be executed on its own.
|
|
105
|
+
* @returns Updated CLI instance.
|
|
106
|
+
*/
|
|
107
|
+
demandCommand(): CLI<TArgs>;
|
|
108
|
+
/**
|
|
109
|
+
* Sets the usage text for the CLI. This text will be displayed in place of the default usage text
|
|
110
|
+
* @param usageText Text displayed in place of the default usage text for `--help` and in generated docs.
|
|
111
|
+
*/
|
|
112
|
+
usage(usageText: string): CLI<TArgs>;
|
|
113
|
+
/**
|
|
114
|
+
* Sets the description for the CLI. This text will be displayed in the help text and generated docs.
|
|
115
|
+
* @param examples Examples to display in the help text and generated docs.
|
|
116
|
+
*/
|
|
117
|
+
examples(...examples: string[]): CLI<TArgs>;
|
|
118
|
+
/**
|
|
119
|
+
* Allows overriding the version displayed when passing `--version`. Defaults to crawling
|
|
120
|
+
* the file system to get the package.json of the currently executing command.
|
|
121
|
+
* @param override
|
|
122
|
+
*/
|
|
123
|
+
version(override?: string): CLI<TArgs>;
|
|
124
|
+
/**
|
|
125
|
+
* Prints help text to stdout.
|
|
126
|
+
*/
|
|
127
|
+
printHelp(): void;
|
|
128
|
+
group({ label, keys, sortOrder, }: {
|
|
129
|
+
label: string;
|
|
130
|
+
keys: (keyof TArgs)[];
|
|
131
|
+
sortOrder: number;
|
|
132
|
+
}): CLI<TArgs>;
|
|
133
|
+
group(label: string, keys: (keyof TArgs)[]): CLI<TArgs>;
|
|
134
|
+
middleware(callback: (args: TArgs) => void): CLI<TArgs>;
|
|
135
|
+
/**
|
|
136
|
+
* Parses argv and executes the CLI
|
|
137
|
+
* @param args argv. Defaults to process.argv.slice(2)
|
|
138
|
+
* @returns Promise that resolves when the handler completes.
|
|
139
|
+
*/
|
|
140
|
+
forge(args?: string[]): Promise<TArgs>;
|
|
141
|
+
}
|
|
142
|
+
export interface CLIHandlerContext {
|
|
143
|
+
command: CLI<any>;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Represents the configuration needed to create a CLI command.
|
|
147
|
+
*/
|
|
148
|
+
export interface CLICommandOptions<
|
|
149
|
+
/**
|
|
150
|
+
* The type of the arguments that are already registered before `builder` is invoked.
|
|
151
|
+
*/
|
|
152
|
+
TInitial extends ParsedArgs,
|
|
153
|
+
/**
|
|
154
|
+
* The type of the arguments that are registered after `builder` is invoked, and the type that is passed to the handler.
|
|
155
|
+
*/
|
|
156
|
+
TArgs extends TInitial = TInitial> {
|
|
157
|
+
/**
|
|
158
|
+
* The command description. This will be displayed in the help text and generated docs.
|
|
159
|
+
*/
|
|
160
|
+
description?: string;
|
|
161
|
+
/**
|
|
162
|
+
* The command builder. This function is called before the command is executed, and is used to register options and positional parameters.
|
|
163
|
+
* @param parser The parser instance to register options and positionals with.
|
|
164
|
+
*/
|
|
165
|
+
builder?: (parser: CLI<TInitial>) => CLI<TArgs>;
|
|
166
|
+
/**
|
|
167
|
+
* The command handler. This function is called when the command is executed.
|
|
168
|
+
* @param args The parsed arguments.
|
|
169
|
+
* @param context Context for the handler. Contains the command instance.
|
|
170
|
+
*/
|
|
171
|
+
handler?: (args: TArgs, context: CLIHandlerContext) => void | Promise<void>;
|
|
172
|
+
/**
|
|
173
|
+
* The usage text for the command. This text will be displayed in place of the default usage text in the help text and generated docs.
|
|
174
|
+
*/
|
|
175
|
+
usage?: string;
|
|
176
|
+
/**
|
|
177
|
+
* Examples to display in the help text and generated docs.
|
|
178
|
+
*/
|
|
179
|
+
examples?: string[];
|
|
180
|
+
/**
|
|
181
|
+
* Hides the command from the help text and generated docs. Useful primarily for experimental or internal commands.
|
|
182
|
+
*/
|
|
183
|
+
hidden?: boolean;
|
|
184
|
+
/**
|
|
185
|
+
* The epilogue text for the command. This text will be displayed at the end of the help text and generated docs.
|
|
186
|
+
*/
|
|
187
|
+
epilogue?: string;
|
|
188
|
+
}
|
|
189
|
+
export type Command<TInitial extends ParsedArgs = any, TArgs extends TInitial = TInitial> = ({
|
|
190
|
+
name: string;
|
|
191
|
+
} & CLICommandOptions<TInitial, TArgs>) | CLI<TArgs>;
|
|
192
|
+
/**
|
|
193
|
+
* Error Handler for CLI applications. Error handlers should re-throw the error if they cannot handle it.
|
|
194
|
+
*
|
|
195
|
+
* @param e The error that was thrown.
|
|
196
|
+
* @param actions Actions that can be taken by the error handler. Prefer using these over process.exit for better support of interactive shells.
|
|
197
|
+
*/
|
|
198
|
+
export type ErrorHandler = (e: unknown, actions: {
|
|
199
|
+
/**
|
|
200
|
+
* Exits the process immediately.
|
|
201
|
+
* @param code
|
|
202
|
+
*/
|
|
203
|
+
exit: (code?: number) => void;
|
|
204
|
+
}) => void;
|
|
205
|
+
/**
|
|
206
|
+
* Constructs a CLI instance. See {@link InternalCLI} for more information.
|
|
207
|
+
* @param name Name for the top level CLI
|
|
208
|
+
* @param rootCommandConfiguration Configuration used when running the bare CLI. e.g. npx my-cli, rather than npx my-cli [cmd]
|
|
209
|
+
* @returns A {@link InternalCLI} instance.
|
|
210
|
+
*/
|
|
211
|
+
export declare function cli<TArgs extends ParsedArgs>(name: string, rootCommandConfiguration?: CLICommandOptions<ParsedArgs, TArgs>): CLI<TArgs>;
|
|
212
|
+
export default cli;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cli = cli;
|
|
4
|
+
const cli_forge_1 = require("./cli-forge");
|
|
5
|
+
/**
|
|
6
|
+
* Constructs a CLI instance. See {@link InternalCLI} for more information.
|
|
7
|
+
* @param name Name for the top level CLI
|
|
8
|
+
* @param rootCommandConfiguration Configuration used when running the bare CLI. e.g. npx my-cli, rather than npx my-cli [cmd]
|
|
9
|
+
* @returns A {@link InternalCLI} instance.
|
|
10
|
+
*/
|
|
11
|
+
function cli(name, rootCommandConfiguration) {
|
|
12
|
+
return new cli_forge_1.InternalCLI(name, rootCommandConfiguration);
|
|
13
|
+
}
|
|
14
|
+
exports.default = cli;
|
|
15
|
+
//# sourceMappingURL=public-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-api.js","sourceRoot":"","sources":["../../../../../packages/cli-forge/src/lib/public-api.ts"],"names":[],"mappings":";;AA4RA,kBAKC;AAhSD,2CAA0C;AAqR1C;;;;;GAKG;AACH,SAAgB,GAAG,CACjB,IAAY,EACZ,wBAA+D;IAE/D,OAAO,IAAI,uBAAW,CAAC,IAAI,EAAE,wBAAwB,CAAsB,CAAC;AAC9E,CAAC;AAED,kBAAe,GAAG,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ParsedArgs } from '@cli-forge/parser';
|
|
2
|
-
import { CLI } from './
|
|
2
|
+
import { CLI } from './public-api';
|
|
3
3
|
export type TestHarnessParseResult<T extends ParsedArgs> = {
|
|
4
4
|
/**
|
|
5
5
|
* Parsed arguments. Note the the typing of this is based on the CLI typings,
|
package/src/lib/test-harness.js
CHANGED
|
@@ -28,7 +28,9 @@ class TestHarness {
|
|
|
28
28
|
exports.TestHarness = TestHarness;
|
|
29
29
|
function mockHandler(cli) {
|
|
30
30
|
if (cli.configuration?.handler) {
|
|
31
|
-
cli.configuration.handler = () => {
|
|
31
|
+
cli.configuration.handler = () => {
|
|
32
|
+
// Mocked, should do nothing.
|
|
33
|
+
};
|
|
32
34
|
}
|
|
33
35
|
for (const command in cli.registeredCommands) {
|
|
34
36
|
mockHandler(cli.registeredCommands[command]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-harness.js","sourceRoot":"","sources":["../../../../../packages/cli-forge/src/lib/test-harness.ts"],"names":[],"mappings":";;;AACA,
|
|
1
|
+
{"version":3,"file":"test-harness.js","sourceRoot":"","sources":["../../../../../packages/cli-forge/src/lib/test-harness.ts"],"names":[],"mappings":";;;AACA,2CAA0C;AA8B1C;;;GAGG;AACH,MAAa,WAAW;IACd,GAAG,CAAiB;IAE5B,YAAY,GAAW;QACrB,IAAI,GAAG,YAAY,uBAAW,EAAE,CAAC;YAC/B,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;YACf,WAAW,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CACb,mEAAmE,CACpE,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAc;QACxB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAExC,OAAO;YACL,IAAI,EAAE,IAAI;YACV,YAAY,EAAE,IAAI,CAAC,GAAG,CAAC,YAAY;SACpC,CAAC;IACJ,CAAC;CACF;AAtBD,kCAsBC;AAED,SAAS,WAAW,CAAC,GAAgB;IACnC,IAAI,GAAG,CAAC,aAAa,EAAE,OAAO,EAAE,CAAC;QAC/B,GAAG,CAAC,aAAa,CAAC,OAAO,GAAG,GAAG,EAAE;YAC/B,6BAA6B;QAC/B,CAAC,CAAC;IACJ,CAAC;IACD,KAAK,MAAM,OAAO,IAAI,GAAG,CAAC,kBAAkB,EAAE,CAAC;QAC7C,WAAW,CAAC,GAAG,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/C,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function getCallingFile(): string | undefined;
|
|
2
|
+
export declare function getParentPackageJson(searchPath: string): {
|
|
3
|
+
name: string;
|
|
4
|
+
version: string;
|
|
5
|
+
bin?: {
|
|
6
|
+
[cmd: string]: string;
|
|
7
|
+
};
|
|
8
|
+
dependencies?: Record<string, string>;
|
|
9
|
+
};
|
|
10
|
+
export declare function stringToArgs(str: string): string[];
|
package/src/lib/utils.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCallingFile = getCallingFile;
|
|
4
|
+
exports.getParentPackageJson = getParentPackageJson;
|
|
5
|
+
exports.stringToArgs = stringToArgs;
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
const path_1 = require("path");
|
|
8
|
+
function getCallingFile() {
|
|
9
|
+
// Since this function lives in a utility file, the parent file
|
|
10
|
+
// would just be the file that invokes getCallingFile... We actually
|
|
11
|
+
// want the parent of the file that calls this, which is this file's
|
|
12
|
+
// grandparent.
|
|
13
|
+
let grandparentFile;
|
|
14
|
+
// Overrides prepare stack trace to be an identity fn, to get the callsites
|
|
15
|
+
// associated with the current error.
|
|
16
|
+
const _pst = Error.prepareStackTrace;
|
|
17
|
+
Error.prepareStackTrace = function (err, stack) {
|
|
18
|
+
return stack;
|
|
19
|
+
};
|
|
20
|
+
try {
|
|
21
|
+
const err = new Error();
|
|
22
|
+
const callsites = err.stack;
|
|
23
|
+
let currentfile = callsites.shift().getFileName();
|
|
24
|
+
let parentfile;
|
|
25
|
+
while (callsites.length) {
|
|
26
|
+
const callerfile = callsites.shift().getFileName();
|
|
27
|
+
// We've reached the parent file
|
|
28
|
+
if (currentfile !== callerfile) {
|
|
29
|
+
// We've reached the grandparent file
|
|
30
|
+
if (parentfile && parentfile !== callerfile) {
|
|
31
|
+
grandparentFile = parentfile;
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
parentfile = callerfile;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
Error.prepareStackTrace = _pst;
|
|
40
|
+
}
|
|
41
|
+
return grandparentFile;
|
|
42
|
+
}
|
|
43
|
+
function getParentPackageJson(searchPath) {
|
|
44
|
+
let currentPath = searchPath;
|
|
45
|
+
let packageJsonPath;
|
|
46
|
+
while (true) {
|
|
47
|
+
const packagePath = (0, path_1.join)(currentPath, 'package.json');
|
|
48
|
+
if ((0, fs_1.existsSync)(packagePath)) {
|
|
49
|
+
packageJsonPath = packagePath;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
const nextPath = (0, path_1.resolve)(currentPath, '..');
|
|
53
|
+
if (nextPath === currentPath) {
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
currentPath = nextPath;
|
|
57
|
+
}
|
|
58
|
+
if (!packageJsonPath) {
|
|
59
|
+
throw new Error('Could not find package.json');
|
|
60
|
+
}
|
|
61
|
+
return require(packageJsonPath);
|
|
62
|
+
}
|
|
63
|
+
function stringToArgs(str) {
|
|
64
|
+
const quotePairs = new Map([
|
|
65
|
+
['"', '"'],
|
|
66
|
+
["'", "'"],
|
|
67
|
+
['`', '`'],
|
|
68
|
+
]);
|
|
69
|
+
const escapeChars = new Set(['\\']);
|
|
70
|
+
let activeQuote;
|
|
71
|
+
const args = [];
|
|
72
|
+
let currentArg = '';
|
|
73
|
+
let prev;
|
|
74
|
+
for (let i = 0; i < str.length; i++) {
|
|
75
|
+
const char = str[i];
|
|
76
|
+
if (activeQuote) {
|
|
77
|
+
while (true) {
|
|
78
|
+
if (i >= str.length) {
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
else if (str[i] === quotePairs.get(activeQuote) &&
|
|
82
|
+
!(prev && escapeChars.has(prev))) {
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
if (!escapeChars.has(str[i])) {
|
|
86
|
+
currentArg += str[i];
|
|
87
|
+
}
|
|
88
|
+
prev = str[i];
|
|
89
|
+
i++;
|
|
90
|
+
}
|
|
91
|
+
activeQuote = undefined;
|
|
92
|
+
}
|
|
93
|
+
else if (quotePairs.has(char) && !(prev && escapeChars.has(prev))) {
|
|
94
|
+
activeQuote = char;
|
|
95
|
+
}
|
|
96
|
+
else if (char === ' ' &&
|
|
97
|
+
prev !== ' ' &&
|
|
98
|
+
!(prev && escapeChars.has(prev))) {
|
|
99
|
+
args.push(currentArg);
|
|
100
|
+
currentArg = '';
|
|
101
|
+
}
|
|
102
|
+
else if (!escapeChars.has(char) || (prev && escapeChars.has(prev))) {
|
|
103
|
+
currentArg += char;
|
|
104
|
+
}
|
|
105
|
+
prev = char;
|
|
106
|
+
}
|
|
107
|
+
args.push(currentArg);
|
|
108
|
+
return args;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../../../packages/cli-forge/src/lib/utils.ts"],"names":[],"mappings":"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"}
|