Skip to content

Commit 95063bb

Browse files
committed
wip: move more to framework
1 parent d7196e8 commit 95063bb

20 files changed

Lines changed: 196 additions & 274 deletions

File tree

packages/@ionic/cli-framework/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
},
3636
"dependencies": {
3737
"chalk": "^2.3.0",
38+
"dargs": "^5.1.0",
3839
"debug": "^3.1.0",
3940
"lodash": "^4.17.4",
4041
"ncp": "^2.0.0",
@@ -44,6 +45,7 @@
4445
"tslib": "^1.8.0"
4546
},
4647
"devDependencies": {
48+
"@types/dargs": "^5.1.0",
4749
"@types/debug": "0.0.30",
4850
"@types/lodash": "^4.14.88",
4951
"@types/minimist": "^1.2.0",

packages/@ionic/cli-framework/src/definitions.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ export interface CommandInput {
2121
private?: boolean;
2222
}
2323

24+
export type CommandOptionTypeDefaults = Map<CommandOptionType, CommandLineInput>;
25+
2426
export interface CommandOption {
2527
name: string;
2628
description: string;
@@ -33,6 +35,19 @@ export interface CommandOption {
3335
advanced?: boolean;
3436
}
3537

38+
export interface NormalizedCommandOption extends CommandOption {
39+
type: CommandOptionType;
40+
default: CommandLineInput;
41+
aliases: string[];
42+
}
43+
44+
export interface NormalizedMinimistOpts extends minimistType.Opts {
45+
string: string[];
46+
boolean: string[];
47+
alias: { [key: string]: string[] };
48+
default: { [key: string]: CommandLineInput };
49+
}
50+
3651
export interface CommandData<T = CommandInput, U = CommandOption> {
3752
name: string;
3853
fullName?: string;

packages/@ionic/cli-utils/src/lib/utils/__tests__/command.ts renamed to packages/@ionic/cli-framework/src/lib/__tests__/command.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import * as minimist from 'minimist';
22

3-
import { CommandData } from '../../../definitions';
43
import { filterOptionsByIntent, metadataToEnvCmdOptsSchema, metadataToMinimistOptions, minimistOptionsToArray } from '../command';
54

6-
describe('@ionic/cli-utils', () => {
5+
describe('@ionic/cli-framework', () => {
76

8-
describe('lib/utils/command', () => {
7+
describe('lib/command', () => {
98

109
describe('minimistOptionsToArray', () => {
1110

@@ -53,7 +52,7 @@ describe('@ionic/cli-utils', () => {
5352

5453
describe('metadataToMinimistOptions', () => {
5554

56-
const metadata: CommandData = {
55+
const metadata = {
5756
fullName: 'foo bar',
5857
name: 'bar',
5958
inputs: [
@@ -115,7 +114,7 @@ describe('@ionic/cli-utils', () => {
115114

116115
describe('metadataToEnvCmdOptsSchema', () => {
117116

118-
const metadata: CommandData = {
117+
const metadata = {
119118
fullName: 'foo bar',
120119
options: [
121120
{
@@ -144,7 +143,7 @@ describe('@ionic/cli-utils', () => {
144143

145144
describe('filterOptionsByIntent', () => {
146145

147-
const metadata: CommandData = {
146+
const metadata = {
148147
description: '',
149148
inputs: [
150149
{

packages/@ionic/cli-framework/src/lib/command.ts

Lines changed: 151 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
1+
import * as dargs from 'dargs';
12
import * as minimist from 'minimist';
23

34
import {
45
CommandData,
6+
CommandLineInput,
57
CommandLineInputs,
68
CommandLineOptions,
9+
CommandOption,
10+
CommandOptionType,
11+
CommandOptionTypeDefaults,
12+
NormalizedCommandOption,
13+
NormalizedMinimistOpts,
714
ValidationError,
815
} from '../definitions';
916

1017
import { validate, validators } from './validators';
1118

19+
export const parseArgs = minimist;
20+
export { ParsedArgs } from 'minimist';
21+
1222
export abstract class Command<T extends CommandData> {
1323
public readonly metadata: T;
1424

@@ -19,6 +29,147 @@ export abstract class Command<T extends CommandData> {
1929
abstract run(inputs: CommandLineInputs, options: CommandLineOptions): Promise<void>;
2030
}
2131

32+
const typeDefaults: CommandOptionTypeDefaults = new Map<CommandOptionType, CommandLineInput>()
33+
.set(String, null)
34+
.set(Boolean, false);
35+
36+
export interface MinimistOptionsToArrayOptions extends dargs.Options {
37+
useDoubleQuotes?: boolean;
38+
}
39+
40+
export function minimistOptionsToArray(options: CommandLineOptions, fnOptions: MinimistOptionsToArrayOptions = {}): string[] {
41+
if (typeof fnOptions.ignoreFalse === 'undefined') {
42+
fnOptions.ignoreFalse = true;
43+
}
44+
45+
if (fnOptions.useDoubleQuotes) {
46+
fnOptions.useEquals = true;
47+
}
48+
49+
let results = dargs(options, fnOptions);
50+
results.splice(results.length - options._.length); // take out arguments
51+
52+
if (fnOptions.useDoubleQuotes) {
53+
results = results.map(r => r.replace(/^(\-\-[A-Za-z0-9-]+)=(.+\s+.+)$/, '$1="$2"'));
54+
}
55+
56+
return results;
57+
}
58+
59+
/**
60+
* Takes a Minimist command option and normalizes its values.
61+
*/
62+
function normalizeOption(option: CommandOption): NormalizedCommandOption {
63+
const type = option.type ? option.type : String;
64+
65+
return {
66+
type,
67+
default: option.default ? option.default : typeDefaults.get(type),
68+
aliases: option.aliases ? option.aliases : [],
69+
...option,
70+
};
71+
}
72+
73+
export function metadataToMinimistOptions(metadata: CommandData): minimist.Opts {
74+
const options: NormalizedMinimistOpts = {
75+
string: ['_'],
76+
boolean: [],
77+
alias: {},
78+
default: {}
79+
};
80+
81+
if (!metadata.options) {
82+
return { boolean: true, string: '_' };
83+
}
84+
85+
const schema = metadataToEnvCmdOptsSchema(metadata);
86+
87+
for (let opt of schema) {
88+
const envvar = process.env[opt.envvar];
89+
90+
if (typeof envvar !== 'undefined') {
91+
if (opt.option.type === Boolean) {
92+
opt.option.default = envvar && envvar !== '0' ? true : false;
93+
} else {
94+
opt.option.default = envvar;
95+
}
96+
}
97+
}
98+
99+
for (let option of metadata.options) {
100+
const normalizedOption = normalizeOption(option);
101+
102+
if (normalizedOption.type === String) {
103+
options.string.push(normalizedOption.name);
104+
} else if (normalizedOption.type === Boolean) {
105+
options.boolean.push(normalizedOption.name);
106+
}
107+
108+
options.default[normalizedOption.name] = normalizedOption.default;
109+
options.alias[normalizedOption.name] = normalizedOption.aliases;
110+
}
111+
112+
return options;
113+
}
114+
115+
export interface CmdOptsSchema {
116+
envvar: string;
117+
option: CommandOption;
118+
}
119+
120+
export function metadataToEnvCmdOptsSchema(metadata: CommandData): CmdOptsSchema[] {
121+
if (!metadata.options) {
122+
return [];
123+
}
124+
125+
const schema: CmdOptsSchema[] = [];
126+
const fullName = metadata.fullName ? metadata.fullName : metadata.name;
127+
const prefix = `IONIC_CMDOPTS_${fullName.toUpperCase().split(' ').join('_')}`;
128+
129+
for (let option of metadata.options) {
130+
schema.push({ envvar: `${prefix}_${option.name.toUpperCase().split('-').join('_')}`, option });
131+
}
132+
133+
return schema;
134+
}
135+
136+
/**
137+
* Filter command line options that match a given "intent", which are specified
138+
* in the command's metadata.
139+
*
140+
* To filter options that have no intent specified in the command's metadata,
141+
* exclude the intentName parameter.
142+
*
143+
* @param metadata
144+
* @param options The options to filter.
145+
* @param indentName
146+
*
147+
* @return The filtered options.
148+
*/
149+
export function filterOptionsByIntent(metadata: CommandData, options: CommandLineOptions, intentName?: string): CommandLineOptions {
150+
const r = Object.keys(options).reduce((allOptions, optionName) => {
151+
const metadataOptionFound = (metadata.options || []).find((mdOption) => (
152+
mdOption.name === optionName || (mdOption.aliases || []).includes(optionName)
153+
));
154+
if (metadataOptionFound) {
155+
if (intentName && metadataOptionFound.intents && metadataOptionFound.intents.includes(intentName)) {
156+
allOptions[optionName] = options[optionName];
157+
} else if (!intentName && !metadataOptionFound.intents) {
158+
allOptions[optionName] = options[optionName];
159+
}
160+
}
161+
return allOptions;
162+
}, <CommandLineOptions>{});
163+
164+
r._ = options._;
165+
166+
if (options['--']) {
167+
r['--'] = options['--'];
168+
}
169+
170+
return r;
171+
}
172+
22173
export function validateInputs(argv: string[], metadata: CommandData): void {
23174
if (!metadata.inputs) {
24175
return;
@@ -46,5 +197,3 @@ export function validateInputs(argv: string[], metadata: CommandData): void {
46197
throw errors;
47198
}
48199
}
49-
50-
export const parseArgs = minimist;

packages/@ionic/cli-framework/tsconfig.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
},
2525
"include": [
2626
"../../types/cross-spawn.d.ts",
27-
"../../types/dargs.d.ts",
2827
"src/**/*.ts"
2928
],
3029
"exclude": [

packages/@ionic/cli-utils/package.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
"chokidar": "^1.7.0",
4747
"ci-info": "^1.1.2",
4848
"cross-spawn": "^5.1.0",
49-
"dargs": "^5.1.0",
5049
"debug": "^3.1.0",
5150
"diff": "^3.4.0",
5251
"elementtree": "^0.1.7",
@@ -55,7 +54,6 @@
5554
"inquirer": "^4.0.0",
5655
"leek": "0.0.24",
5756
"lodash": "^4.17.4",
58-
"minimist": "^1.2.0",
5957
"opn": "^5.1.0",
6058
"os-name": "^2.0.1",
6159
"semver": "^5.4.1",
@@ -86,7 +84,6 @@
8684
"@types/http-proxy-middleware": "^0.17.2",
8785
"@types/inquirer": "0.0.35",
8886
"@types/lodash": "^4.14.85",
89-
"@types/minimist": "^1.2.0",
9087
"@types/opn": "^5.1.0",
9188
"@types/semver": "^5.4.0",
9289
"@types/split2": "^2.1.6",

packages/@ionic/cli-utils/src/definitions.ts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { EventEmitter } from 'events';
44
import * as crossSpawnType from 'cross-spawn';
55
import * as inquirerType from 'inquirer';
66
import * as superagentType from 'superagent';
7-
import * as minimistType from 'minimist';
87

98
import * as framework from '@ionic/cli-framework';
109

@@ -262,29 +261,14 @@ export interface IDaemon extends IBaseConfig<DaemonFile> {
262261
setPort(port: number): Promise<void>;
263262
}
264263

265-
export type CommandOptionTypeDefaults = Map<framework.CommandOptionType, framework.CommandLineInput>;
266-
267264
export interface CommandOption extends framework.CommandOption {
268265
backends?: BackendFlag[];
269266
}
270267

271-
export interface NormalizedCommandOption extends CommandOption {
272-
type: framework.CommandOptionType;
273-
default: framework.CommandLineInput;
274-
aliases: string[];
275-
}
276-
277268
export interface ExitCodeException extends Error {
278269
exitCode: number;
279270
}
280271

281-
export interface NormalizedMinimistOpts extends minimistType.Opts {
282-
string: string[];
283-
boolean: string[];
284-
alias: { [key: string]: string[] };
285-
default: { [key: string]: framework.CommandLineInput };
286-
}
287-
288272
export type BackendFlag = 'pro' | 'legacy';
289273

290274
export interface CommandData extends framework.CommandData<framework.CommandInput, CommandOption> {

packages/@ionic/cli-utils/src/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import * as util from 'util';
22
import * as path from 'path';
33

4-
import { isCI } from 'ci-info';
54
import chalk from 'chalk';
6-
import * as minimist from 'minimist';
5+
import { isCI } from 'ci-info';
6+
import { parseArgs } from '@ionic/cli-framework/lib';
7+
import { findBaseDirectory } from '@ionic/cli-framework/utils/fs';
78

89
import * as inquirerType from 'inquirer';
910

@@ -32,7 +33,6 @@ import { Environment } from './lib/environment';
3233
import { HookEngine } from './lib/hooks';
3334
import { PROJECT_FILE, PROJECT_FILE_LEGACY, Project } from './lib/project';
3435
import { Logger } from './lib/utils/logger';
35-
import { findBaseDirectory } from '@ionic/cli-framework/utils/fs';
3636
import { InteractiveTaskChain, TaskChain } from './lib/utils/task';
3737
import { readPackageJsonFileOfResolvedModule } from './lib/utils/npm';
3838
import { Telemetry } from './lib/telemetry';
@@ -163,7 +163,7 @@ async function getSession(config: IConfig, project: IProject, client: IClient):
163163

164164
export async function generateIonicEnvironment(plugin: RootPlugin, pargv: string[], env: { [key: string]: string }): Promise<IonicEnvironment> {
165165
const cwd = process.cwd();
166-
const argv = minimist(pargv, { boolean: true, string: '_' });
166+
const argv = parseArgs(pargv, { boolean: true, string: '_' });
167167
const config = new Config(env['IONIC_CONFIG_DIRECTORY'] || DEFAULT_CONFIG_DIRECTORY, CONFIG_FILE);
168168
const flags = gatherFlags(argv);
169169

packages/@ionic/cli-utils/src/lib/command.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import chalk from 'chalk';
22

3+
import {
4+
Command as BaseCommand,
5+
minimistOptionsToArray,
6+
validateInputs,
7+
validators,
8+
} from '@ionic/cli-framework/lib';
9+
310
import {
411
CommandData,
512
CommandLineInputs,
@@ -9,9 +16,6 @@ import {
916
} from '../definitions';
1017

1118
import { isCommandPreRun } from '../guards';
12-
import { validators } from '@ionic/cli-framework/lib';
13-
import { minimistOptionsToArray } from './utils/command';
14-
import { Command as BaseCommand, validateInputs } from '@ionic/cli-framework/lib';
1519

1620
export function CommandMetadata(metadata: CommandData) {
1721
return function(target: Function) {

0 commit comments

Comments
 (0)