@guanghechen/commander 4.6.0 → 4.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/CHANGELOG.md +7 -0
- package/README.md +13 -15
- package/lib/cjs/browser.cjs +1779 -0
- package/lib/cjs/{index.cjs → node.cjs} +105 -29
- package/lib/esm/browser.mjs +1764 -0
- package/lib/esm/{index.mjs → node.mjs} +104 -13
- package/lib/types/browser.d.ts +551 -0
- package/lib/types/{index.d.ts → node.d.ts} +91 -47
- package/package.json +15 -12
|
@@ -1,30 +1,98 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var env = require('@guanghechen/env');
|
|
4
|
-
var reporter = require('@guanghechen/reporter');
|
|
5
3
|
var promises = require('node:fs/promises');
|
|
6
4
|
var path = require('node:path');
|
|
5
|
+
var env = require('@guanghechen/env');
|
|
6
|
+
var reporter = require('@guanghechen/reporter');
|
|
7
7
|
var fs = require('node:fs');
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
9
|
+
const WINDOWS_DRIVE_ABSOLUTE_REGEX = /^[a-zA-Z]:[\\/]/;
|
|
10
|
+
function isAbsolutePath(filepath) {
|
|
11
|
+
return (filepath.startsWith('/') ||
|
|
12
|
+
filepath.startsWith('\\\\') ||
|
|
13
|
+
WINDOWS_DRIVE_ABSOLUTE_REGEX.test(filepath));
|
|
14
|
+
}
|
|
15
|
+
function resolvePathFrom(base, fragment) {
|
|
16
|
+
const useWindowsStyle = WINDOWS_DRIVE_ABSOLUTE_REGEX.test(base);
|
|
17
|
+
const normalizedBase = base.replace(/\\/g, '/');
|
|
18
|
+
const normalizedFragment = fragment.replace(/\\/g, '/');
|
|
19
|
+
const source = isAbsolutePath(normalizedFragment)
|
|
20
|
+
? normalizedFragment
|
|
21
|
+
: `${normalizedBase.replace(/\/$/, '')}/${normalizedFragment}`;
|
|
22
|
+
const prefix = useWindowsStyle ? source.slice(0, 2) : '';
|
|
23
|
+
const body = useWindowsStyle ? source.slice(2) : source;
|
|
24
|
+
const stack = [];
|
|
25
|
+
for (const token of body.split('/')) {
|
|
26
|
+
if (token === '' || token === '.') {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (token === '..') {
|
|
30
|
+
if (stack.length > 0) {
|
|
31
|
+
stack.pop();
|
|
19
32
|
}
|
|
20
|
-
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
stack.push(token);
|
|
21
36
|
}
|
|
22
|
-
|
|
23
|
-
|
|
37
|
+
if (useWindowsStyle) {
|
|
38
|
+
const resolved = `${prefix}/${stack.join('/')}`;
|
|
39
|
+
return resolved.endsWith('/') ? resolved.slice(0, -1) : resolved;
|
|
40
|
+
}
|
|
41
|
+
return `/${stack.join('/')}`;
|
|
42
|
+
}
|
|
43
|
+
function createUnsupportedFsError(operation) {
|
|
44
|
+
return new Error(`runtime does not support file-system operation: ${operation}`);
|
|
45
|
+
}
|
|
46
|
+
function getFallbackCwd() {
|
|
47
|
+
const proc = globalThis.process;
|
|
48
|
+
if (proc && typeof proc.cwd === 'function') {
|
|
49
|
+
return proc.cwd();
|
|
50
|
+
}
|
|
51
|
+
return '/';
|
|
52
|
+
}
|
|
53
|
+
function createBrowserCommandRuntime() {
|
|
54
|
+
return {
|
|
55
|
+
cwd: () => getFallbackCwd(),
|
|
56
|
+
isAbsolute: filepath => isAbsolutePath(filepath),
|
|
57
|
+
resolve: (...paths) => {
|
|
58
|
+
if (paths.length === 0) {
|
|
59
|
+
return getFallbackCwd();
|
|
60
|
+
}
|
|
61
|
+
let resolved = getFallbackCwd();
|
|
62
|
+
for (const path of paths) {
|
|
63
|
+
if (path.length === 0) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
resolved = resolvePathFrom(resolved, path);
|
|
67
|
+
}
|
|
68
|
+
return resolved;
|
|
69
|
+
},
|
|
70
|
+
readFile: async () => {
|
|
71
|
+
throw createUnsupportedFsError('readFile');
|
|
72
|
+
},
|
|
73
|
+
stat: async () => {
|
|
74
|
+
throw createUnsupportedFsError('stat');
|
|
75
|
+
},
|
|
76
|
+
};
|
|
24
77
|
}
|
|
25
78
|
|
|
26
|
-
|
|
27
|
-
|
|
79
|
+
let defaultRuntime = createBrowserCommandRuntime();
|
|
80
|
+
function getDefaultCommandRuntime() {
|
|
81
|
+
return defaultRuntime;
|
|
82
|
+
}
|
|
83
|
+
function setDefaultCommandRuntime(runtime) {
|
|
84
|
+
defaultRuntime = runtime;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function createNodeCommandRuntime() {
|
|
88
|
+
return {
|
|
89
|
+
cwd: () => process.cwd(),
|
|
90
|
+
isAbsolute: filepath => path.isAbsolute(filepath),
|
|
91
|
+
resolve: (...paths) => path.resolve(...paths),
|
|
92
|
+
readFile: filepath => promises.readFile(filepath, 'utf8'),
|
|
93
|
+
stat: filepath => promises.stat(filepath),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
28
96
|
|
|
29
97
|
const TERMINAL_STYLE = {
|
|
30
98
|
bold: '\x1b[1m',
|
|
@@ -291,6 +359,7 @@ class Command {
|
|
|
291
359
|
#builtin;
|
|
292
360
|
#presetConfig;
|
|
293
361
|
#reporter;
|
|
362
|
+
#runtime;
|
|
294
363
|
#parent;
|
|
295
364
|
#options = [];
|
|
296
365
|
#arguments = [];
|
|
@@ -306,6 +375,7 @@ class Command {
|
|
|
306
375
|
this.#builtin = normalizeBuiltinConfig(config.builtin);
|
|
307
376
|
this.#presetConfig = config.preset;
|
|
308
377
|
this.#reporter = config.reporter;
|
|
378
|
+
this.#runtime = config.runtime ?? getDefaultCommandRuntime();
|
|
309
379
|
}
|
|
310
380
|
get name() {
|
|
311
381
|
return this.#name || undefined;
|
|
@@ -824,12 +894,12 @@ class Command {
|
|
|
824
894
|
return await this.#assertPresetRoot(commandPreset.root, 'command.preset.root', commandPath);
|
|
825
895
|
}
|
|
826
896
|
async #assertPresetRoot(root, sourceName, commandPath) {
|
|
827
|
-
if (!
|
|
897
|
+
if (!this.#runtime.isAbsolute(root)) {
|
|
828
898
|
throw new CommanderError('ConfigurationError', `invalid preset root from "${sourceName}": "${root}" is not an absolute directory`, commandPath);
|
|
829
899
|
}
|
|
830
900
|
let stats;
|
|
831
901
|
try {
|
|
832
|
-
stats = await
|
|
902
|
+
stats = await this.#runtime.stat(root);
|
|
833
903
|
}
|
|
834
904
|
catch (error) {
|
|
835
905
|
throw new CommanderError('ConfigurationError', `invalid preset root from "${sourceName}": "${root}" cannot be accessed (${error.message})`, commandPath);
|
|
@@ -869,7 +939,7 @@ class Command {
|
|
|
869
939
|
},
|
|
870
940
|
];
|
|
871
941
|
}
|
|
872
|
-
const absolutePath =
|
|
942
|
+
const absolutePath = this.#runtime.resolve(presetRoot, defaultFilename);
|
|
873
943
|
return [
|
|
874
944
|
{
|
|
875
945
|
displayPath: absolutePath,
|
|
@@ -879,13 +949,13 @@ class Command {
|
|
|
879
949
|
];
|
|
880
950
|
}
|
|
881
951
|
#resolvePresetFileAbsolutePath(filepath, presetRoot) {
|
|
882
|
-
if (
|
|
952
|
+
if (this.#runtime.isAbsolute(filepath)) {
|
|
883
953
|
return filepath;
|
|
884
954
|
}
|
|
885
955
|
if (presetRoot !== undefined) {
|
|
886
|
-
return
|
|
956
|
+
return this.#runtime.resolve(presetRoot, filepath);
|
|
887
957
|
}
|
|
888
|
-
return
|
|
958
|
+
return this.#runtime.resolve(this.#runtime.cwd(), filepath);
|
|
889
959
|
}
|
|
890
960
|
#assertPresetOptionFragments(tokens, filepath, chain, optionPolicyMap) {
|
|
891
961
|
if (tokens.length === 0) {
|
|
@@ -998,14 +1068,14 @@ class Command {
|
|
|
998
1068
|
}
|
|
999
1069
|
async #readPresetFile(file, commandPath) {
|
|
1000
1070
|
try {
|
|
1001
|
-
return await
|
|
1071
|
+
return await this.#runtime.readFile(file.absolutePath);
|
|
1002
1072
|
}
|
|
1003
1073
|
catch (error) {
|
|
1004
1074
|
const ioError = error;
|
|
1005
1075
|
if (!file.explicit && ioError.code === 'ENOENT') {
|
|
1006
1076
|
return undefined;
|
|
1007
1077
|
}
|
|
1008
|
-
throw new CommanderError('ConfigurationError', `failed to read preset file "${file.displayPath}": ${
|
|
1078
|
+
throw new CommanderError('ConfigurationError', `failed to read preset file "${file.displayPath}": ${error.message}`, commandPath);
|
|
1009
1079
|
}
|
|
1010
1080
|
}
|
|
1011
1081
|
#tokenizePresetOptions(content) {
|
|
@@ -1775,11 +1845,11 @@ class CompletionCommand extends Command {
|
|
|
1775
1845
|
if (writeOpt !== undefined) {
|
|
1776
1846
|
const filePath = typeof writeOpt === 'string' && writeOpt !== '' ? writeOpt : paths[shell];
|
|
1777
1847
|
const expandedPath = expandHome(filePath);
|
|
1778
|
-
const dir =
|
|
1779
|
-
if (!
|
|
1780
|
-
|
|
1848
|
+
const dir = path.dirname(expandedPath);
|
|
1849
|
+
if (!fs.existsSync(dir)) {
|
|
1850
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1781
1851
|
}
|
|
1782
|
-
|
|
1852
|
+
fs.writeFileSync(expandedPath, script, 'utf-8');
|
|
1783
1853
|
console.log(`Completion script written to: ${expandedPath}`);
|
|
1784
1854
|
}
|
|
1785
1855
|
else {
|
|
@@ -2072,6 +2142,8 @@ class PwshCompletion {
|
|
|
2072
2142
|
}
|
|
2073
2143
|
}
|
|
2074
2144
|
|
|
2145
|
+
setDefaultCommandRuntime(createNodeCommandRuntime());
|
|
2146
|
+
|
|
2075
2147
|
exports.BashCompletion = BashCompletion;
|
|
2076
2148
|
exports.Coerce = Coerce;
|
|
2077
2149
|
exports.Command = Command;
|
|
@@ -2079,6 +2151,9 @@ exports.CommanderError = CommanderError;
|
|
|
2079
2151
|
exports.CompletionCommand = CompletionCommand;
|
|
2080
2152
|
exports.FishCompletion = FishCompletion;
|
|
2081
2153
|
exports.PwshCompletion = PwshCompletion;
|
|
2154
|
+
exports.createBrowserCommandRuntime = createBrowserCommandRuntime;
|
|
2155
|
+
exports.createNodeCommandRuntime = createNodeCommandRuntime;
|
|
2156
|
+
exports.getDefaultCommandRuntime = getDefaultCommandRuntime;
|
|
2082
2157
|
exports.isDomain = isDomain;
|
|
2083
2158
|
exports.isIp = isIp;
|
|
2084
2159
|
exports.isIpv4 = isIpv4;
|
|
@@ -2086,4 +2161,5 @@ exports.isIpv6 = isIpv6;
|
|
|
2086
2161
|
exports.logColorfulOption = logColorfulOption;
|
|
2087
2162
|
exports.logDateOption = logDateOption;
|
|
2088
2163
|
exports.logLevelOption = logLevelOption;
|
|
2164
|
+
exports.setDefaultCommandRuntime = setDefaultCommandRuntime;
|
|
2089
2165
|
exports.silentOption = silentOption;
|