@git.zone/cli 2.13.15 → 2.14.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.
Files changed (39) hide show
  1. package/dist_ts/00_commitinfo_data.js +2 -2
  2. package/dist_ts/gitzone.cli.js +46 -35
  3. package/dist_ts/helpers.climode.d.ts +22 -0
  4. package/dist_ts/helpers.climode.js +158 -0
  5. package/dist_ts/helpers.smartconfig.d.ts +11 -0
  6. package/dist_ts/helpers.smartconfig.js +139 -0
  7. package/dist_ts/mod_commit/index.d.ts +2 -0
  8. package/dist_ts/mod_commit/index.js +204 -92
  9. package/dist_ts/mod_config/index.d.ts +7 -2
  10. package/dist_ts/mod_config/index.js +390 -176
  11. package/dist_ts/mod_format/classes.formatcontext.d.ts +11 -2
  12. package/dist_ts/mod_format/classes.formatcontext.js +14 -4
  13. package/dist_ts/mod_format/formatters/packagejson.formatter.js +1 -67
  14. package/dist_ts/mod_format/formatters/smartconfig.formatter.d.ts +2 -2
  15. package/dist_ts/mod_format/formatters/smartconfig.formatter.js +46 -39
  16. package/dist_ts/mod_format/index.d.ts +4 -1
  17. package/dist_ts/mod_format/index.js +221 -75
  18. package/dist_ts/mod_format/mod.plugins.d.ts +1 -2
  19. package/dist_ts/mod_format/mod.plugins.js +2 -3
  20. package/dist_ts/mod_services/index.d.ts +2 -0
  21. package/dist_ts/mod_services/index.js +366 -168
  22. package/dist_ts/mod_standard/index.d.ts +3 -1
  23. package/dist_ts/mod_standard/index.js +159 -59
  24. package/package.json +1 -1
  25. package/readme.hints.md +25 -32
  26. package/readme.md +87 -65
  27. package/ts/00_commitinfo_data.ts +1 -1
  28. package/ts/gitzone.cli.ts +50 -38
  29. package/ts/helpers.climode.ts +212 -0
  30. package/ts/helpers.smartconfig.ts +192 -0
  31. package/ts/mod_commit/index.ts +325 -98
  32. package/ts/mod_config/index.ts +490 -182
  33. package/ts/mod_format/classes.formatcontext.ts +20 -3
  34. package/ts/mod_format/formatters/packagejson.formatter.ts +0 -91
  35. package/ts/mod_format/formatters/smartconfig.formatter.ts +55 -39
  36. package/ts/mod_format/index.ts +279 -90
  37. package/ts/mod_format/mod.plugins.ts +0 -2
  38. package/ts/mod_services/index.ts +550 -183
  39. package/ts/mod_standard/index.ts +191 -58
package/ts/gitzone.cli.ts CHANGED
@@ -1,23 +1,29 @@
1
- import * as plugins from './plugins.js';
2
- import * as paths from './paths.js';
3
- import { GitzoneConfig } from './classes.gitzoneconfig.js';
1
+ import * as plugins from "./plugins.js";
2
+ import * as paths from "./paths.js";
3
+ import { GitzoneConfig } from "./classes.gitzoneconfig.js";
4
+ import { getRawCliMode } from "./helpers.climode.js";
4
5
 
5
6
  const gitzoneSmartcli = new plugins.smartcli.Smartcli();
6
7
 
7
8
  export let run = async () => {
8
9
  const done = plugins.smartpromise.defer();
10
+ const rawCliMode = await getRawCliMode();
9
11
 
10
12
  // get packageInfo
11
13
  const projectInfo = new plugins.projectinfo.ProjectInfo(paths.packageDir);
12
14
 
13
15
  // check for updates
14
- const smartupdateInstance = new plugins.smartupdate.SmartUpdate();
15
- await smartupdateInstance.check(
16
- 'gitzone',
17
- projectInfo.npm.version,
18
- 'http://gitzone.gitlab.io/gitzone/changelog.html',
19
- );
20
- console.log('---------------------------------------------');
16
+ if (rawCliMode.checkUpdates) {
17
+ const smartupdateInstance = new plugins.smartupdate.SmartUpdate();
18
+ await smartupdateInstance.check(
19
+ "gitzone",
20
+ projectInfo.npm.version,
21
+ "http://gitzone.gitlab.io/gitzone/changelog.html",
22
+ );
23
+ }
24
+ if (rawCliMode.output === "human") {
25
+ console.log("---------------------------------------------");
26
+ }
21
27
  gitzoneSmartcli.addVersion(projectInfo.npm.version);
22
28
 
23
29
  // ======> Standard task <======
@@ -26,8 +32,13 @@ export let run = async () => {
26
32
  * standard task
27
33
  */
28
34
  gitzoneSmartcli.standardCommand().subscribe(async (argvArg) => {
29
- const modStandard = await import('./mod_standard/index.js');
30
- await modStandard.run();
35
+ const modStandard = await import("./mod_standard/index.js");
36
+ await modStandard.run(argvArg);
37
+ });
38
+
39
+ gitzoneSmartcli.addCommand("help").subscribe(async (argvArg) => {
40
+ const modStandard = await import("./mod_standard/index.js");
41
+ await modStandard.run(argvArg);
31
42
  });
32
43
 
33
44
  // ======> Specific tasks <======
@@ -35,43 +46,44 @@ export let run = async () => {
35
46
  /**
36
47
  * commit something
37
48
  */
38
- gitzoneSmartcli.addCommand('commit').subscribe(async (argvArg) => {
39
- const modCommit = await import('./mod_commit/index.js');
49
+ gitzoneSmartcli.addCommand("commit").subscribe(async (argvArg) => {
50
+ const modCommit = await import("./mod_commit/index.js");
40
51
  await modCommit.run(argvArg);
41
52
  });
42
53
 
43
54
  /**
44
55
  * deprecate a package on npm
45
56
  */
46
- gitzoneSmartcli.addCommand('deprecate').subscribe(async (argvArg) => {
47
- const modDeprecate = await import('./mod_deprecate/index.js');
57
+ gitzoneSmartcli.addCommand("deprecate").subscribe(async (argvArg) => {
58
+ const modDeprecate = await import("./mod_deprecate/index.js");
48
59
  await modDeprecate.run();
49
60
  });
50
61
 
51
62
  /**
52
63
  * docker
53
64
  */
54
- gitzoneSmartcli.addCommand('docker').subscribe(async (argvArg) => {
55
- const modDocker = await import('./mod_docker/index.js');
65
+ gitzoneSmartcli.addCommand("docker").subscribe(async (argvArg) => {
66
+ const modDocker = await import("./mod_docker/index.js");
56
67
  await modDocker.run(argvArg);
57
68
  });
58
69
 
59
70
  /**
60
71
  * Update all files that comply with the gitzone standard
61
72
  */
62
- gitzoneSmartcli.addCommand('format').subscribe(async (argvArg) => {
73
+ gitzoneSmartcli.addCommand("format").subscribe(async (argvArg) => {
63
74
  const config = GitzoneConfig.fromCwd();
64
- const modFormat = await import('./mod_format/index.js');
75
+ const modFormat = await import("./mod_format/index.js");
65
76
 
66
77
  // Handle format with options
67
78
  // Default is dry-mode, use --write/-w to apply changes
68
79
  await modFormat.run({
80
+ ...argvArg,
69
81
  write: argvArg.write || argvArg.w,
70
- dryRun: argvArg['dry-run'],
82
+ dryRun: argvArg["dry-run"],
71
83
  yes: argvArg.yes,
72
- planOnly: argvArg['plan-only'],
73
- savePlan: argvArg['save-plan'],
74
- fromPlan: argvArg['from-plan'],
84
+ planOnly: argvArg["plan-only"],
85
+ savePlan: argvArg["save-plan"],
86
+ fromPlan: argvArg["from-plan"],
75
87
  detailed: argvArg.detailed,
76
88
  interactive: argvArg.interactive !== false,
77
89
  verbose: argvArg.verbose,
@@ -82,54 +94,54 @@ export let run = async () => {
82
94
  /**
83
95
  * run meta commands
84
96
  */
85
- gitzoneSmartcli.addCommand('meta').subscribe(async (argvArg) => {
97
+ gitzoneSmartcli.addCommand("meta").subscribe(async (argvArg) => {
86
98
  const config = GitzoneConfig.fromCwd();
87
- const modMeta = await import('./mod_meta/index.js');
99
+ const modMeta = await import("./mod_meta/index.js");
88
100
  modMeta.run(argvArg);
89
101
  });
90
102
 
91
103
  /**
92
104
  * open assets
93
105
  */
94
- gitzoneSmartcli.addCommand('open').subscribe(async (argvArg) => {
95
- const modOpen = await import('./mod_open/index.js');
106
+ gitzoneSmartcli.addCommand("open").subscribe(async (argvArg) => {
107
+ const modOpen = await import("./mod_open/index.js");
96
108
  modOpen.run(argvArg);
97
109
  });
98
110
 
99
111
  /**
100
112
  * add a readme to a project
101
113
  */
102
- gitzoneSmartcli.addCommand('template').subscribe(async (argvArg) => {
103
- const modTemplate = await import('./mod_template/index.js');
114
+ gitzoneSmartcli.addCommand("template").subscribe(async (argvArg) => {
115
+ const modTemplate = await import("./mod_template/index.js");
104
116
  modTemplate.run(argvArg);
105
117
  });
106
118
 
107
119
  /**
108
120
  * start working on a project
109
121
  */
110
- gitzoneSmartcli.addCommand('start').subscribe(async (argvArg) => {
111
- const modTemplate = await import('./mod_start/index.js');
122
+ gitzoneSmartcli.addCommand("start").subscribe(async (argvArg) => {
123
+ const modTemplate = await import("./mod_start/index.js");
112
124
  modTemplate.run(argvArg);
113
125
  });
114
126
 
115
- gitzoneSmartcli.addCommand('helpers').subscribe(async (argvArg) => {
116
- const modHelpers = await import('./mod_helpers/index.js');
127
+ gitzoneSmartcli.addCommand("helpers").subscribe(async (argvArg) => {
128
+ const modHelpers = await import("./mod_helpers/index.js");
117
129
  modHelpers.run(argvArg);
118
130
  });
119
131
 
120
132
  /**
121
133
  * manage release configuration
122
134
  */
123
- gitzoneSmartcli.addCommand('config').subscribe(async (argvArg) => {
124
- const modConfig = await import('./mod_config/index.js');
135
+ gitzoneSmartcli.addCommand("config").subscribe(async (argvArg) => {
136
+ const modConfig = await import("./mod_config/index.js");
125
137
  await modConfig.run(argvArg);
126
138
  });
127
139
 
128
140
  /**
129
141
  * manage development services (MongoDB, S3/MinIO)
130
142
  */
131
- gitzoneSmartcli.addCommand('services').subscribe(async (argvArg) => {
132
- const modServices = await import('./mod_services/index.js');
143
+ gitzoneSmartcli.addCommand("services").subscribe(async (argvArg) => {
144
+ const modServices = await import("./mod_services/index.js");
133
145
  await modServices.run(argvArg);
134
146
  });
135
147
 
@@ -0,0 +1,212 @@
1
+ import { getCliConfigValue } from "./helpers.smartconfig.js";
2
+
3
+ export type TCliOutputMode = "human" | "plain" | "json";
4
+
5
+ export interface ICliMode {
6
+ output: TCliOutputMode;
7
+ interactive: boolean;
8
+ json: boolean;
9
+ plain: boolean;
10
+ quiet: boolean;
11
+ yes: boolean;
12
+ help: boolean;
13
+ agent: boolean;
14
+ checkUpdates: boolean;
15
+ isTty: boolean;
16
+ command?: string;
17
+ }
18
+
19
+ interface ICliConfigSettings {
20
+ interactive?: boolean;
21
+ output?: TCliOutputMode;
22
+ checkUpdates?: boolean;
23
+ }
24
+
25
+ type TArgSource = Record<string, any> & { _?: string[] };
26
+
27
+ const camelCase = (value: string): string => {
28
+ return value.replace(/-([a-z])/g, (_match, group: string) =>
29
+ group.toUpperCase(),
30
+ );
31
+ };
32
+
33
+ const getArgValue = (argvArg: TArgSource, key: string): any => {
34
+ const keyVariants = [key, camelCase(key), key.replace(/-/g, "")];
35
+ for (const keyVariant of keyVariants) {
36
+ if (argvArg[keyVariant] !== undefined) {
37
+ return argvArg[keyVariant];
38
+ }
39
+ }
40
+ return undefined;
41
+ };
42
+
43
+ const parseRawArgv = (argv: string[]): TArgSource => {
44
+ const parsedArgv: TArgSource = { _: [] };
45
+
46
+ for (let i = 0; i < argv.length; i++) {
47
+ const currentArg = argv[i];
48
+
49
+ if (currentArg.startsWith("--no-")) {
50
+ const key = currentArg.slice(5);
51
+ parsedArgv[key] = false;
52
+ parsedArgv[camelCase(key)] = false;
53
+ continue;
54
+ }
55
+
56
+ if (currentArg.startsWith("--")) {
57
+ const withoutPrefix = currentArg.slice(2);
58
+ const [rawKey, inlineValue] = withoutPrefix.split("=", 2);
59
+ if (inlineValue !== undefined) {
60
+ parsedArgv[rawKey] = inlineValue;
61
+ parsedArgv[camelCase(rawKey)] = inlineValue;
62
+ continue;
63
+ }
64
+
65
+ const nextArg = argv[i + 1];
66
+ if (nextArg && !nextArg.startsWith("-")) {
67
+ parsedArgv[rawKey] = nextArg;
68
+ parsedArgv[camelCase(rawKey)] = nextArg;
69
+ i++;
70
+ } else {
71
+ parsedArgv[rawKey] = true;
72
+ parsedArgv[camelCase(rawKey)] = true;
73
+ }
74
+ continue;
75
+ }
76
+
77
+ if (currentArg.startsWith("-") && currentArg.length > 1) {
78
+ for (const shortFlag of currentArg.slice(1).split("")) {
79
+ parsedArgv[shortFlag] = true;
80
+ }
81
+ continue;
82
+ }
83
+
84
+ parsedArgv._ = parsedArgv._ || [];
85
+ parsedArgv._.push(currentArg);
86
+ }
87
+
88
+ return parsedArgv;
89
+ };
90
+
91
+ const normalizeOutputMode = (value: unknown): TCliOutputMode | undefined => {
92
+ if (value === "human" || value === "plain" || value === "json") {
93
+ return value;
94
+ }
95
+ return undefined;
96
+ };
97
+
98
+ const resolveCliMode = (
99
+ argvArg: TArgSource,
100
+ cliConfig: ICliConfigSettings,
101
+ ): ICliMode => {
102
+ const isTty = Boolean(process.stdout?.isTTY && process.stdin?.isTTY);
103
+ const agentMode = Boolean(getArgValue(argvArg, "agent"));
104
+ const outputOverride = normalizeOutputMode(getArgValue(argvArg, "output"));
105
+
106
+ let output: TCliOutputMode =
107
+ normalizeOutputMode(cliConfig.output) || (isTty ? "human" : "plain");
108
+ if (agentMode || getArgValue(argvArg, "json")) {
109
+ output = "json";
110
+ } else if (getArgValue(argvArg, "plain")) {
111
+ output = "plain";
112
+ } else if (outputOverride) {
113
+ output = outputOverride;
114
+ }
115
+
116
+ const interactiveSetting = getArgValue(argvArg, "interactive");
117
+ let interactive = cliConfig.interactive ?? isTty;
118
+ if (interactiveSetting === true) {
119
+ interactive = true;
120
+ } else if (interactiveSetting === false) {
121
+ interactive = false;
122
+ }
123
+ if (!isTty || output !== "human" || agentMode) {
124
+ interactive = false;
125
+ }
126
+
127
+ const checkUpdatesSetting = getArgValue(argvArg, "check-updates");
128
+ let checkUpdates = cliConfig.checkUpdates ?? output === "human";
129
+ if (checkUpdatesSetting === true) {
130
+ checkUpdates = true;
131
+ } else if (checkUpdatesSetting === false) {
132
+ checkUpdates = false;
133
+ }
134
+ if (output !== "human" || agentMode) {
135
+ checkUpdates = false;
136
+ }
137
+
138
+ return {
139
+ output,
140
+ interactive,
141
+ json: output === "json",
142
+ plain: output === "plain",
143
+ quiet: Boolean(
144
+ getArgValue(argvArg, "quiet") ||
145
+ getArgValue(argvArg, "q") ||
146
+ output === "json",
147
+ ),
148
+ yes: Boolean(getArgValue(argvArg, "yes") || getArgValue(argvArg, "y")),
149
+ help: Boolean(
150
+ getArgValue(argvArg, "help") ||
151
+ getArgValue(argvArg, "h") ||
152
+ argvArg._?.[0] === "help",
153
+ ),
154
+ agent: agentMode,
155
+ checkUpdates,
156
+ isTty,
157
+ command: argvArg._?.[0],
158
+ };
159
+ };
160
+
161
+ const getCliModeConfig = async (): Promise<ICliConfigSettings> => {
162
+ return await getCliConfigValue<ICliConfigSettings>("cli", {});
163
+ };
164
+
165
+ export const getCliMode = async (
166
+ argvArg: TArgSource = {},
167
+ ): Promise<ICliMode> => {
168
+ const cliConfig = await getCliModeConfig();
169
+ return resolveCliMode(argvArg, cliConfig);
170
+ };
171
+
172
+ export const getRawCliMode = async (): Promise<ICliMode> => {
173
+ const cliConfig = await getCliModeConfig();
174
+ const rawArgv = parseRawArgv(process.argv.slice(2));
175
+ return resolveCliMode(rawArgv, cliConfig);
176
+ };
177
+
178
+ export const printJson = (data: unknown): void => {
179
+ console.log(JSON.stringify(data, null, 2));
180
+ };
181
+
182
+ export const runWithSuppressedOutput = async <T>(
183
+ fn: () => Promise<T>,
184
+ ): Promise<T> => {
185
+ const originalConsole = {
186
+ log: console.log,
187
+ info: console.info,
188
+ warn: console.warn,
189
+ error: console.error,
190
+ };
191
+ const originalStdoutWrite = process.stdout.write.bind(process.stdout);
192
+ const originalStderrWrite = process.stderr.write.bind(process.stderr);
193
+ const noop = () => undefined;
194
+
195
+ console.log = noop;
196
+ console.info = noop;
197
+ console.warn = noop;
198
+ console.error = noop;
199
+ process.stdout.write = (() => true) as typeof process.stdout.write;
200
+ process.stderr.write = (() => true) as typeof process.stderr.write;
201
+
202
+ try {
203
+ return await fn();
204
+ } finally {
205
+ console.log = originalConsole.log;
206
+ console.info = originalConsole.info;
207
+ console.warn = originalConsole.warn;
208
+ console.error = originalConsole.error;
209
+ process.stdout.write = originalStdoutWrite;
210
+ process.stderr.write = originalStderrWrite;
211
+ }
212
+ };
@@ -0,0 +1,192 @@
1
+ import * as plugins from "./plugins.js";
2
+ import { rename, writeFile } from "fs/promises";
3
+
4
+ export const CLI_NAMESPACE = "@git.zone/cli";
5
+
6
+ const isPlainObject = (value: unknown): value is Record<string, any> => {
7
+ return typeof value === "object" && value !== null && !Array.isArray(value);
8
+ };
9
+
10
+ export const getSmartconfigPath = (cwd: string = process.cwd()): string => {
11
+ return plugins.path.join(cwd, ".smartconfig.json");
12
+ };
13
+
14
+ export const readSmartconfigFile = async (
15
+ cwd: string = process.cwd(),
16
+ ): Promise<Record<string, any>> => {
17
+ const smartconfigPath = getSmartconfigPath(cwd);
18
+ if (!(await plugins.smartfs.file(smartconfigPath).exists())) {
19
+ return {};
20
+ }
21
+
22
+ const content = (await plugins.smartfs
23
+ .file(smartconfigPath)
24
+ .encoding("utf8")
25
+ .read()) as string;
26
+ if (content.trim() === "") {
27
+ return {};
28
+ }
29
+ return JSON.parse(content);
30
+ };
31
+
32
+ export const writeSmartconfigFile = async (
33
+ data: Record<string, any>,
34
+ cwd: string = process.cwd(),
35
+ ): Promise<void> => {
36
+ const smartconfigPath = getSmartconfigPath(cwd);
37
+ const tempPath = `${smartconfigPath}.tmp-${Date.now()}`;
38
+ const content = JSON.stringify(data, null, 2);
39
+ await writeFile(tempPath, content, "utf8");
40
+ await rename(tempPath, smartconfigPath);
41
+ };
42
+
43
+ export const normalizeCliConfigPath = (configPath: string): string => {
44
+ const trimmedPath = configPath.trim();
45
+ if (!trimmedPath || trimmedPath === CLI_NAMESPACE) {
46
+ return "";
47
+ }
48
+
49
+ if (trimmedPath.startsWith(`${CLI_NAMESPACE}.`)) {
50
+ return trimmedPath.slice(`${CLI_NAMESPACE}.`.length);
51
+ }
52
+
53
+ return trimmedPath;
54
+ };
55
+
56
+ export const getCliConfigPathSegments = (configPath: string): string[] => {
57
+ const normalizedPath = normalizeCliConfigPath(configPath);
58
+ if (!normalizedPath) {
59
+ return [];
60
+ }
61
+
62
+ return normalizedPath
63
+ .split(".")
64
+ .map((segment) => segment.trim())
65
+ .filter(Boolean);
66
+ };
67
+
68
+ export const getCliNamespaceConfig = (
69
+ smartconfigData: Record<string, any>,
70
+ ): Record<string, any> => {
71
+ const cliConfig = smartconfigData[CLI_NAMESPACE];
72
+ if (isPlainObject(cliConfig)) {
73
+ return cliConfig;
74
+ }
75
+ return {};
76
+ };
77
+
78
+ export const getCliConfigValueFromData = (
79
+ smartconfigData: Record<string, any>,
80
+ configPath: string,
81
+ ): any => {
82
+ const segments = getCliConfigPathSegments(configPath);
83
+ let currentValue: any = getCliNamespaceConfig(smartconfigData);
84
+
85
+ for (const segment of segments) {
86
+ if (!isPlainObject(currentValue) && !Array.isArray(currentValue)) {
87
+ return undefined;
88
+ }
89
+ currentValue = (currentValue as any)?.[segment];
90
+ }
91
+
92
+ return currentValue;
93
+ };
94
+
95
+ export const getCliConfigValue = async <T>(
96
+ configPath: string,
97
+ defaultValue: T,
98
+ cwd: string = process.cwd(),
99
+ ): Promise<T> => {
100
+ const smartconfigData = await readSmartconfigFile(cwd);
101
+ const configValue = getCliConfigValueFromData(smartconfigData, configPath);
102
+
103
+ if (configValue === undefined) {
104
+ return defaultValue;
105
+ }
106
+
107
+ if (isPlainObject(defaultValue) && isPlainObject(configValue)) {
108
+ return {
109
+ ...defaultValue,
110
+ ...configValue,
111
+ } as T;
112
+ }
113
+
114
+ return configValue as T;
115
+ };
116
+
117
+ export const setCliConfigValueInData = (
118
+ smartconfigData: Record<string, any>,
119
+ configPath: string,
120
+ value: any,
121
+ ): Record<string, any> => {
122
+ const segments = getCliConfigPathSegments(configPath);
123
+
124
+ if (!isPlainObject(smartconfigData[CLI_NAMESPACE])) {
125
+ smartconfigData[CLI_NAMESPACE] = {};
126
+ }
127
+
128
+ if (segments.length === 0) {
129
+ smartconfigData[CLI_NAMESPACE] = value;
130
+ return smartconfigData;
131
+ }
132
+
133
+ let currentValue = smartconfigData[CLI_NAMESPACE];
134
+ for (const segment of segments.slice(0, -1)) {
135
+ if (!isPlainObject(currentValue[segment])) {
136
+ currentValue[segment] = {};
137
+ }
138
+ currentValue = currentValue[segment];
139
+ }
140
+
141
+ currentValue[segments[segments.length - 1]] = value;
142
+ return smartconfigData;
143
+ };
144
+
145
+ export const unsetCliConfigValueInData = (
146
+ smartconfigData: Record<string, any>,
147
+ configPath: string,
148
+ ): boolean => {
149
+ const segments = getCliConfigPathSegments(configPath);
150
+ if (segments.length === 0) {
151
+ if (smartconfigData[CLI_NAMESPACE] !== undefined) {
152
+ delete smartconfigData[CLI_NAMESPACE];
153
+ return true;
154
+ }
155
+ return false;
156
+ }
157
+
158
+ const parentSegments = segments.slice(0, -1);
159
+ let currentValue: any = getCliNamespaceConfig(smartconfigData);
160
+ const objectPath: Array<Record<string, any>> = [currentValue];
161
+
162
+ for (const segment of parentSegments) {
163
+ if (!isPlainObject(currentValue[segment])) {
164
+ return false;
165
+ }
166
+ currentValue = currentValue[segment];
167
+ objectPath.push(currentValue);
168
+ }
169
+
170
+ const lastSegment = segments[segments.length - 1];
171
+ if (!(lastSegment in currentValue)) {
172
+ return false;
173
+ }
174
+
175
+ delete currentValue[lastSegment];
176
+
177
+ for (let i = objectPath.length - 1; i >= 1; i--) {
178
+ if (Object.keys(objectPath[i]).length > 0) {
179
+ break;
180
+ }
181
+
182
+ const parentObject = objectPath[i - 1];
183
+ const parentKey = parentSegments[i - 1];
184
+ delete parentObject[parentKey];
185
+ }
186
+
187
+ if (Object.keys(getCliNamespaceConfig(smartconfigData)).length === 0) {
188
+ delete smartconfigData[CLI_NAMESPACE];
189
+ }
190
+
191
+ return true;
192
+ };