-
Notifications
You must be signed in to change notification settings - Fork 26
/
Copy pathpreparse.ts
115 lines (103 loc) · 3.98 KB
/
preparse.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/*
* Copyright (c) 2024, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
import type { Hook } from '@oclif/core/hooks';
const unescapedComma = /(?<!\\),/;
const hook: Hook.Preparse = async function ({ argv, options, context }) {
if (unescapedComma.test(argv.join(' '))) {
const hasArrayFlag = Object.values(options.flags ?? {}).some(
(flagOptions) => flagOptions.type === 'option' && flagOptions.multiple === true && flagOptions.delimiter === ','
);
if (hasArrayFlag) {
const { Lifecycle } = await import('@salesforce/core/lifecycle');
await Lifecycle.getInstance().emitWarning(
'The input format for array arguments has changed. Use this format: --array-flag value1 --array-flag value2 --array-flag value3'
);
}
}
// Skip this hook if command does not have a --flags-dir flag or if it is not present in argv
if (!argv.includes('--flags-dir') || !options.flags?.['flags-dir']) return argv;
const flagsDir = argv[argv.indexOf('--flags-dir') + 1];
if (!flagsDir) {
context.debug('No flags dir provided');
return argv;
}
if (flagsDir.startsWith('-')) {
context.debug(`No flags dir provided, got ${flagsDir}`);
return argv;
}
const { default: fs } = await import('node:fs/promises');
const { default: path } = await import('node:path');
context.debug('Initial argv', argv.join(' '));
const flagsToIgnore = new Set(
Object.entries(options.flags ?? {})
.filter(
([, flagOptions]) =>
// don't ignore if flag can take multiple values
(flagOptions.type === 'option' && flagOptions.multiple !== true) || flagOptions.type === 'boolean'
)
.filter(
([flagName, flagOptions]) =>
// ignore if short char flag is present
argv.includes(`-${flagOptions.char}`) ||
// ignore if long flag is present
argv.includes(`--${flagName}`) ||
// ignore if --no- flag is present
(flagOptions.type === 'boolean' && flagOptions.allowNo && argv.includes(`--no-${flagName}`))
)
.flatMap(([flagName, flagOptions]) => {
// Also ignore the --no- flag if boolean flag allows it
if (flagOptions.type === 'boolean' && flagOptions.allowNo) {
return [flagName, `no-${flagName}`];
}
return [flagName];
})
);
context.debug('Flags to ignore', flagsToIgnore);
async function safeReadDir(filePath: string): Promise<string[]> {
try {
return await fs.readdir(filePath);
} catch (err) {
context.debug('No flags dir found');
context.debug(err);
return [];
}
}
async function safeReadFile(filePath: string): Promise<string | undefined> {
try {
return await fs.readFile(filePath, 'utf8');
} catch (err) {
context.debug(filePath, err);
}
}
const filesInDir = await safeReadDir(flagsDir);
context.debug('Files in dir', filesInDir);
const flagsToInsert = await Promise.all(
filesInDir
// ignore files that were provided as flags
.filter((f) => !flagsToIgnore.has(f))
.map(async (file) => {
const { name, ext } = path.parse(file);
const contents = await safeReadFile(path.join(flagsDir, file));
if (contents === undefined) {
return [name, undefined] satisfies [string, undefined];
}
const values = ext === '.json' ? [JSON.stringify(JSON.parse(contents))] : contents?.trim().split('\n');
return [name, values] satisfies [string, string[]];
})
);
const newArgv = [...argv];
context.debug('Flags to insert', flagsToInsert);
for (const [flag, values] of flagsToInsert) {
for (const value of values ?? []) {
newArgv.push(flag.length === 1 ? `-${flag}` : `--${flag}`);
if (value) newArgv.push(value);
}
}
context.debug(`Returning argv: ${newArgv.join(' ')}`);
return newArgv;
};
export default hook;