@mikeyt23/node-cli-utils 1.4.1 → 2.0.0-beta.1

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 (64) hide show
  1. package/README.md +45 -87
  2. package/dist/cjs/NodeCliUtilsConfig.d.ts +21 -0
  3. package/dist/cjs/NodeCliUtilsConfig.d.ts.map +1 -0
  4. package/dist/cjs/NodeCliUtilsConfig.js +41 -0
  5. package/dist/cjs/TarballUtility.d.ts +53 -0
  6. package/dist/cjs/TarballUtility.d.ts.map +1 -0
  7. package/dist/cjs/TarballUtility.js +149 -0
  8. package/dist/cjs/certUtils.d.ts +30 -0
  9. package/dist/cjs/certUtils.d.ts.map +1 -0
  10. package/dist/cjs/certUtils.js +219 -0
  11. package/dist/cjs/dbMigrationUtils.d.ts +39 -0
  12. package/dist/cjs/dbMigrationUtils.d.ts.map +1 -0
  13. package/dist/cjs/dbMigrationUtils.js +205 -0
  14. package/dist/cjs/dotnetUtils.d.ts +25 -0
  15. package/dist/cjs/dotnetUtils.d.ts.map +1 -0
  16. package/dist/cjs/dotnetUtils.js +59 -0
  17. package/dist/cjs/esmSpecific.d.mts +2 -0
  18. package/dist/cjs/esmSpecific.d.mts.map +1 -0
  19. package/dist/cjs/esmSpecific.mjs +10 -0
  20. package/dist/cjs/generalUtils.d.ts +323 -0
  21. package/dist/cjs/generalUtils.d.ts.map +1 -0
  22. package/dist/cjs/generalUtils.js +652 -0
  23. package/dist/cjs/generalUtilsInternal.d.ts +9 -0
  24. package/dist/cjs/generalUtilsInternal.d.ts.map +1 -0
  25. package/dist/cjs/generalUtilsInternal.js +217 -0
  26. package/dist/cjs/index.d.ts +4 -0
  27. package/dist/cjs/index.d.ts.map +1 -0
  28. package/dist/cjs/index.js +25 -0
  29. package/dist/cjs/package.json +5 -0
  30. package/dist/cjs/runWhileParentAlive.d.ts +2 -0
  31. package/dist/cjs/runWhileParentAlive.d.ts.map +1 -0
  32. package/dist/cjs/runWhileParentAlive.js +161 -0
  33. package/dist/esm/NodeCliUtilsConfig.d.ts +21 -0
  34. package/dist/esm/NodeCliUtilsConfig.d.ts.map +1 -0
  35. package/dist/esm/NodeCliUtilsConfig.js +35 -0
  36. package/dist/esm/TarballUtility.d.ts +53 -0
  37. package/dist/esm/TarballUtility.d.ts.map +1 -0
  38. package/dist/esm/TarballUtility.js +143 -0
  39. package/dist/esm/certUtils.d.ts +30 -0
  40. package/dist/esm/certUtils.d.ts.map +1 -0
  41. package/dist/esm/certUtils.js +185 -0
  42. package/dist/esm/dbMigrationUtils.d.ts +39 -0
  43. package/dist/esm/dbMigrationUtils.d.ts.map +1 -0
  44. package/dist/esm/dbMigrationUtils.js +194 -0
  45. package/dist/esm/dotnetUtils.d.ts +25 -0
  46. package/dist/esm/dotnetUtils.d.ts.map +1 -0
  47. package/dist/esm/dotnetUtils.js +52 -0
  48. package/dist/esm/esmSpecific.d.mts +2 -0
  49. package/dist/esm/esmSpecific.d.mts.map +1 -0
  50. package/dist/esm/esmSpecific.mjs +6 -0
  51. package/dist/esm/generalUtils.d.ts +323 -0
  52. package/dist/esm/generalUtils.d.ts.map +1 -0
  53. package/dist/esm/generalUtils.js +591 -0
  54. package/dist/esm/generalUtilsInternal.d.ts +9 -0
  55. package/dist/esm/generalUtilsInternal.d.ts.map +1 -0
  56. package/dist/esm/generalUtilsInternal.js +185 -0
  57. package/dist/esm/index.d.ts +4 -0
  58. package/dist/esm/index.d.ts.map +1 -0
  59. package/dist/esm/index.js +4 -0
  60. package/dist/esm/runWhileParentAlive.d.ts +2 -0
  61. package/dist/esm/runWhileParentAlive.d.ts.map +1 -0
  62. package/dist/esm/runWhileParentAlive.js +153 -0
  63. package/package.json +67 -10
  64. package/index.js +0 -627
@@ -0,0 +1,652 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.escapeStringForRegex = exports.findFilesRecursively = exports.deleteEnvIfExists = exports.sortDictionaryByKeyAsc = exports.filterDictionary = exports.copyModifiedEnv = exports.overwriteEnvFile = exports.copyNewEnvValues = exports.getConfirmationExample = exports.getConfirmation = exports.askQuestion = exports.isDockerRunning = exports.whichSync = exports.isPlatformLinux = exports.isPlatformMac = exports.isPlatformWindows = exports.simpleSpawnSync = exports.simpleCmdSync = exports.stringToNonEmptyLines = exports.spawnDockerCompose = exports.requireValidPath = exports.requireString = exports.copyDirectoryContents = exports.emptyDirectory = exports.mkdirp = exports.ensureDirectory = exports.spawnAsyncLongRunning = exports.spawnAsync = exports.sleep = exports.SimpleSpawnError = exports.SpawnError = exports.trace = exports.log = void 0;
30
+ const node_child_process_1 = require("node:child_process");
31
+ const node_fs_1 = __importDefault(require("node:fs"));
32
+ const promises_1 = __importDefault(require("node:fs/promises"));
33
+ const node_os_1 = require("node:os");
34
+ const node_path_1 = __importStar(require("node:path"));
35
+ const readline = __importStar(require("readline"));
36
+ const NodeCliUtilsConfig_js_1 = require("./NodeCliUtilsConfig.js");
37
+ const generalUtilsInternal_js_1 = require("./generalUtilsInternal.js");
38
+ const dockerComposeCommandsThatSupportDetached = ['exec', 'logs', 'ps', 'restart', 'run', 'start', 'stop', 'up'];
39
+ /**
40
+ * Just a wrapper for console.log() to type less.
41
+ * @param data The data to log
42
+ * @param moreData More data to log
43
+ */
44
+ function log(data, ...moreData) {
45
+ console.log(data, ...moreData);
46
+ }
47
+ exports.log = log;
48
+ /**
49
+ * Wrapper for console.log() that is suppressed if NodeCliUtilsConfig.logEnabled is false.
50
+ * @param data The data to log
51
+ * @param moreData More data to log
52
+ */
53
+ function trace(data, ...moreData) {
54
+ if (NodeCliUtilsConfig_js_1.config.traceEnabled) {
55
+ const prefix = `[TRACE]`;
56
+ console.log(prefix, data, ...moreData);
57
+ }
58
+ }
59
+ exports.trace = trace;
60
+ /**
61
+ * Error throw by {@link spawnAsync} when the spawned process exits with a non-zero exit code and options.throwOnNonZero is true.
62
+ *
63
+ * Contains a {@link SpawnResult} with the exit code, stdout, stderr, and error (if any).
64
+ */
65
+ class SpawnError extends Error {
66
+ constructor(message, result) {
67
+ super(message);
68
+ this.result = result;
69
+ }
70
+ }
71
+ exports.SpawnError = SpawnError;
72
+ /**
73
+ * Error throw by {@link simpleSpawnSync} and {@link simpleCmdSync} when the spawned process exits with a non-zero exit code and throwOnNonZero param is true (the default).
74
+ *
75
+ * Contains a {@link SimpleSpawnResult} with the exit code, stdout, stderr, and error (if any) in addition to stdoutLines, which is stdout split into lines from stdout that weren't empty.
76
+ */
77
+ class SimpleSpawnError extends Error {
78
+ constructor(message, result) {
79
+ super(message);
80
+ this.result = result;
81
+ }
82
+ }
83
+ exports.SimpleSpawnError = SimpleSpawnError;
84
+ /**
85
+ * Sleeps for the specified number of milliseconds.
86
+ * @param ms The number of milliseconds to sleep
87
+ * @returns A Promise that resolves after the specified number of milliseconds
88
+ */
89
+ async function sleep(ms) {
90
+ return new Promise((resolve) => {
91
+ setTimeout(resolve, ms);
92
+ });
93
+ }
94
+ exports.sleep = sleep;
95
+ /**
96
+ * This is a wrapper function for NodeJS. Defaults stdio to inherit so that output is visible in the console,
97
+ * but note that this means stdout and stderr will not be available in the returned SpawnResult.
98
+ *
99
+ * When spawning long-running processes, use {@link spawnAsyncLongRunning} instead so that unexpected
100
+ * termination of the parent process will not orphan the child process tree on windows.
101
+ * @param command The command to spawn
102
+ * @param args The arguments to pass to the command
103
+ * @param options The options to pass to the command
104
+ * @returns A Promise that resolves to a {@link SpawnResult}
105
+ */
106
+ async function spawnAsync(command, args, options) {
107
+ return (0, generalUtilsInternal_js_1.spawnAsyncInternal)(command, args, options);
108
+ }
109
+ exports.spawnAsync = spawnAsync;
110
+ /**
111
+ * Use this alternate spawn wrapper instead of {@link spawnAsync} when spawning long-running processes to
112
+ * avoid orphaned child process trees on Windows.
113
+ * @param command The command to spawn
114
+ * @param args The arguments to pass to the command
115
+ * @param cwd The current working directory to run the command from - defaults to process.cwd()
116
+ * @returns A Promise that resolves to a {@link SpawnResult}
117
+ */
118
+ async function spawnAsyncLongRunning(command, args, cwd) {
119
+ return (0, generalUtilsInternal_js_1.spawnAsyncInternal)(command, args, { cwd: cwd, isLongRunning: true });
120
+ }
121
+ exports.spawnAsyncLongRunning = spawnAsyncLongRunning;
122
+ /**
123
+ * Ensure the directory exists. Similar to `mkdir -p` (creates parent directories if they don't exist).
124
+ * @param dir The directory to ensure exists. If it does not exist, it will be created.
125
+ */
126
+ async function ensureDirectory(dir) {
127
+ requireString('dir', dir);
128
+ if (!node_fs_1.default.existsSync(dir)) {
129
+ await mkdirp(dir);
130
+ }
131
+ }
132
+ exports.ensureDirectory = ensureDirectory;
133
+ /**
134
+ * Create a directory. Will create parent directory structure if it don't exist. Similar to `mkdir -p`.
135
+ * @param dir The directory to create.
136
+ */
137
+ async function mkdirp(dir) {
138
+ await promises_1.default.mkdir(dir, { recursive: true });
139
+ }
140
+ exports.mkdirp = mkdirp;
141
+ /**
142
+ * Empties a directory of all files and subdirectories.
143
+ *
144
+ * Optionally skips files and directories at the top level.
145
+ * @param directoryToEmpty The directory to empty.
146
+ * @param fileAndDirectoryNamesToSkip An optional array of file and directory names to skip, but only at the top level of the directoryToEmpty.
147
+ */
148
+ async function emptyDirectory(directoryToEmpty, fileAndDirectoryNamesToSkip) {
149
+ requireString('directoryToEmpty', directoryToEmpty);
150
+ if (!node_fs_1.default.existsSync(directoryToEmpty)) {
151
+ trace(`directoryToEmpty does not exist - creating directory ${directoryToEmpty}`);
152
+ await mkdirp(directoryToEmpty);
153
+ return;
154
+ }
155
+ if (!node_fs_1.default.lstatSync(directoryToEmpty).isDirectory()) {
156
+ throw new Error(`directoryToEmpty is not a directory: ${directoryToEmpty}`);
157
+ }
158
+ // Add some guardrails to prevent accidentally emptying the wrong directory
159
+ const absolutePath = node_path_1.default.resolve(directoryToEmpty);
160
+ log(`emptying directory: ${absolutePath}`);
161
+ if (!absolutePath.startsWith(process.cwd())) {
162
+ throw new Error(`directoryToEmpty must be a child of the current working directory: ${directoryToEmpty}`);
163
+ }
164
+ if (absolutePath === process.cwd()) {
165
+ throw new Error(`directoryToEmpty cannot be the current working directory: ${directoryToEmpty}`);
166
+ }
167
+ const dir = await promises_1.default.opendir(directoryToEmpty, { encoding: 'utf-8' });
168
+ if (fileAndDirectoryNamesToSkip && !Array.isArray(fileAndDirectoryNamesToSkip)) {
169
+ throw new Error('fileAndDirectoryNamesToSkip must be an array');
170
+ }
171
+ let dirEntry = await dir.read();
172
+ while (dirEntry) {
173
+ if (fileAndDirectoryNamesToSkip && fileAndDirectoryNamesToSkip.includes(dirEntry.name)) {
174
+ dirEntry = await dir.read();
175
+ continue;
176
+ }
177
+ const direntPath = node_path_1.default.join(directoryToEmpty, dirEntry.name);
178
+ if (dirEntry.isDirectory()) {
179
+ await promises_1.default.rm(direntPath, { recursive: true });
180
+ }
181
+ else {
182
+ await promises_1.default.unlink(direntPath);
183
+ }
184
+ dirEntry = await dir.read();
185
+ }
186
+ await dir.close();
187
+ }
188
+ exports.emptyDirectory = emptyDirectory;
189
+ /**
190
+ * Copies the contents of a directory to another directory (not including the top-level directory itself).
191
+ *
192
+ * If the destination directory does not exist, it will be created.
193
+ * @param sourceDirectory Directory to copy from
194
+ * @param destinationDirectory Directory to copy to
195
+ */
196
+ async function copyDirectoryContents(sourceDirectory, destinationDirectory) {
197
+ requireString('sourceDirectory', sourceDirectory);
198
+ requireString('destinationDirectory', destinationDirectory);
199
+ if (!node_fs_1.default.existsSync(sourceDirectory)) {
200
+ throw new Error(`sourceDirectory directory does not exist: ${sourceDirectory}`);
201
+ }
202
+ if (!node_fs_1.default.lstatSync(sourceDirectory).isDirectory()) {
203
+ throw new Error(`sourceDirectory is not a directory: ${sourceDirectory}`);
204
+ }
205
+ if (!node_fs_1.default.existsSync(destinationDirectory)) {
206
+ await mkdirp(destinationDirectory);
207
+ }
208
+ if (!node_fs_1.default.lstatSync(destinationDirectory).isDirectory()) {
209
+ throw new Error(`destinationDirectory is not a directory: ${destinationDirectory}`);
210
+ }
211
+ const dir = await promises_1.default.opendir(sourceDirectory, { encoding: 'utf-8' });
212
+ let dirEntry = await dir.read();
213
+ while (dirEntry) {
214
+ const sourcePath = node_path_1.default.join(sourceDirectory, dirEntry.name);
215
+ const destPath = node_path_1.default.join(destinationDirectory, dirEntry.name);
216
+ if (dirEntry.isDirectory()) {
217
+ await copyDirectoryContents(sourcePath, destPath);
218
+ }
219
+ else {
220
+ await promises_1.default.copyFile(sourcePath, destPath);
221
+ }
222
+ dirEntry = await dir.read();
223
+ }
224
+ }
225
+ exports.copyDirectoryContents = copyDirectoryContents;
226
+ /**
227
+ * Helper method to validate that a non-falsy value is provided for a parameter that should be a string.
228
+ *
229
+ * **Warning:** this does not validate the type of the parameter, just whether something non-empty was provided.
230
+ * @param paramName The name of the parameter, for logging purposes
231
+ * @param paramValue The value of the parameter
232
+ */
233
+ function requireString(paramName, paramValue) {
234
+ if (paramValue === undefined || paramValue === null || paramValue === '') {
235
+ throw new Error(`Required param '${paramName}' is missing`);
236
+ }
237
+ if (typeof paramValue !== 'string') {
238
+ throw new Error(`Required param '${paramName}' is not a string`);
239
+ }
240
+ }
241
+ exports.requireString = requireString;
242
+ /**
243
+ * Helper method to validate that the path actually exists for the provided value.
244
+ * @param paramName The name of the parameter, for logging purposes
245
+ * @param paramValue The value of the parameter
246
+ */
247
+ function requireValidPath(paramName, paramValue) {
248
+ requireString(paramName, paramValue);
249
+ if (!node_fs_1.default.existsSync(paramValue)) {
250
+ throw new Error(`Invalid or nonexistent path provided for param '${paramName}': ${paramValue}`);
251
+ }
252
+ }
253
+ exports.requireValidPath = requireValidPath;
254
+ /**
255
+ * For docker compose commands, see https://docs.docker.com/compose/reference/.
256
+ * @param dockerComposePath Path to docker-compose.yml
257
+ * @param dockerComposeCommand The docker-compose command to run
258
+ * @param options {@link DockerComposeOptions} to use, including additional arguments to pass to the docker compose command and the project name
259
+ */
260
+ async function spawnDockerCompose(dockerComposePath, dockerComposeCommand, options) {
261
+ requireValidPath('dockerComposePath', dockerComposePath);
262
+ requireString('dockerComposeCommand', dockerComposeCommand);
263
+ const useDockerComposeFileDirectoryAsCwd = options && options.useDockerComposeFileDirectoryAsCwd;
264
+ if (await isDockerRunning() === false) {
265
+ throw new Error('Docker is not running');
266
+ }
267
+ const defaultOptions = { attached: false };
268
+ const mergedOptions = { ...defaultOptions, ...options };
269
+ const dockerComposeDir = node_path_1.default.dirname(dockerComposePath);
270
+ const dockerComposeFilename = node_path_1.default.basename(dockerComposePath);
271
+ const spawnCommand = 'docker';
272
+ let spawnArgs = ['compose', '-f', useDockerComposeFileDirectoryAsCwd ? dockerComposeFilename : dockerComposePath];
273
+ if (mergedOptions.projectName) {
274
+ spawnArgs.push('--project-name', mergedOptions.projectName);
275
+ }
276
+ spawnArgs.push(dockerComposeCommand);
277
+ if (!mergedOptions.attached && dockerComposeCommandsThatSupportDetached.includes(dockerComposeCommand)) {
278
+ spawnArgs.push('-d');
279
+ }
280
+ if (mergedOptions.args) {
281
+ spawnArgs = spawnArgs.concat(mergedOptions.args);
282
+ }
283
+ const dockerCommandString = `docker ${spawnArgs.join(' ')}`;
284
+ const traceMessage = useDockerComposeFileDirectoryAsCwd ?
285
+ `running command in ${dockerComposeDir}: ${dockerCommandString}` :
286
+ `running command: ${dockerCommandString}`;
287
+ trace(traceMessage);
288
+ const longRunning = dockerComposeCommandsThatSupportDetached.includes(dockerComposeCommand) && options && options.attached;
289
+ const spawnOptions = {
290
+ cwd: useDockerComposeFileDirectoryAsCwd ? dockerComposeDir : process.cwd(),
291
+ shell: true,
292
+ isLongRunning: longRunning
293
+ };
294
+ const spawnResult = await (0, generalUtilsInternal_js_1.spawnAsyncInternal)(spawnCommand, spawnArgs, spawnOptions);
295
+ if (spawnResult.code !== 0) {
296
+ throw new Error(`docker compose command failed with code ${spawnResult.code}`);
297
+ }
298
+ }
299
+ exports.spawnDockerCompose = spawnDockerCompose;
300
+ /**
301
+ * Splits a string into lines, removing empty lines and carriage return characters.
302
+ * @param str String to split into lines
303
+ * @returns An array of lines from the string, with empty lines removed
304
+ */
305
+ function stringToNonEmptyLines(str) {
306
+ if (!str) {
307
+ return [];
308
+ }
309
+ return str.split('\n').filter(line => line && line.trim()).map(line => line.replace('\r', ''));
310
+ }
311
+ exports.stringToNonEmptyLines = stringToNonEmptyLines;
312
+ /**
313
+ * Runs the requested command using NodeJS spawnSync wrapped in an outer Windows CMD.exe command and returns the result with stdout split into lines.
314
+ *
315
+ * Use this for simple quick commands that don't require a lot of control.
316
+ *
317
+ * For commands that aren't Windows and CMD specific, use {@link simpleSpawnSync}.
318
+ * @param command Command to run
319
+ * @param args Arguments to pass to the command
320
+ * @returns An object with the status code, stdout, stderr, and error (if any)
321
+ * @throws {@link SimpleSpawnError} if the command fails and throwOnNonZero is true
322
+ */
323
+ function simpleCmdSync(command, args, throwOnNonZero = true) {
324
+ if (!isPlatformWindows()) {
325
+ throw new Error('getCmdResult is only supported on Windows');
326
+ }
327
+ return simpleSpawnSync('cmd', ['/D', '/S', '/C', command, ...(args !== null && args !== void 0 ? args : [])], throwOnNonZero);
328
+ }
329
+ exports.simpleCmdSync = simpleCmdSync;
330
+ /**
331
+ * Runs the requested command using NodeJS spawnSync and returns the result with stdout split into lines.
332
+ *
333
+ * Use this for simple quick commands that don't require a lot of control.
334
+ *
335
+ * For commands that are Windows and CMD specific, use {@link simpleCmdSync}.
336
+ * @param command Command to run
337
+ * @param args Arguments to pass to the command
338
+ * @returns An object with the status code, stdout, stderr, and error (if any)
339
+ * @throws {@link SimpleSpawnError} if the command fails and throwOnNonZero is true
340
+ */
341
+ function simpleSpawnSync(command, args, throwOnNonZero = true) {
342
+ var _a;
343
+ requireString('command', command);
344
+ const result = (0, node_child_process_1.spawnSync)(command, args !== null && args !== void 0 ? args : [], { encoding: 'utf-8' });
345
+ const spawnResult = {
346
+ code: (_a = result.status) !== null && _a !== void 0 ? _a : 1,
347
+ stdout: result.stdout.toString(),
348
+ stderr: result.stdout.toString(),
349
+ stdoutLines: stringToNonEmptyLines(result.stdout.toString()),
350
+ error: result.error,
351
+ cwd: process.cwd()
352
+ };
353
+ if (spawnResult.code !== 0 && throwOnNonZero) {
354
+ throw new SimpleSpawnError(`spawned process failed with code ${spawnResult.code}`, spawnResult);
355
+ }
356
+ return spawnResult;
357
+ }
358
+ exports.simpleSpawnSync = simpleSpawnSync;
359
+ /**
360
+ * @returns `true` if platform() is 'win32', `false` otherwise
361
+ */
362
+ function isPlatformWindows() {
363
+ return (0, node_os_1.platform)() === 'win32';
364
+ }
365
+ exports.isPlatformWindows = isPlatformWindows;
366
+ /**
367
+ *
368
+ * @returns `true` if platform() is 'darwin', `false` otherwise
369
+ */
370
+ function isPlatformMac() {
371
+ return (0, node_os_1.platform)() === 'darwin';
372
+ }
373
+ exports.isPlatformMac = isPlatformMac;
374
+ /**
375
+ *
376
+ * @returns `true` if {@link isPlatformWindows} and {@link isPlatformMac} are both `false, otherwise returns `false`
377
+ */
378
+ function isPlatformLinux() {
379
+ return !isPlatformWindows() && !isPlatformMac();
380
+ }
381
+ exports.isPlatformLinux = isPlatformLinux;
382
+ /**
383
+ * This is a cross-platform method to get the location of a system command. Useful for checking if software
384
+ * is installed, where it's installed and whether there are multiple locations for a command.
385
+ * @param commandName The name of the command to find
386
+ * @returns The location of the command, any additional locations, and an error if one occurred
387
+ */
388
+ function whichSync(commandName) {
389
+ if (isPlatformWindows()) {
390
+ const result = simpleCmdSync('where', [commandName]);
391
+ return {
392
+ location: result.stdoutLines[0],
393
+ additionalLocations: result.stdoutLines.slice(1),
394
+ error: result.error
395
+ };
396
+ }
397
+ else {
398
+ const result = simpleSpawnSync('which', ['-a', commandName]);
399
+ return {
400
+ location: result.stdoutLines[0],
401
+ additionalLocations: result.stdoutLines.slice(1),
402
+ error: result.error
403
+ };
404
+ }
405
+ }
406
+ exports.whichSync = whichSync;
407
+ /**
408
+ * First checks if docker is installed and if not immediately returns false.
409
+ *
410
+ * Then runs the `docker info` command and looks for "error during connect" in the output to determine if docker is running.
411
+ * @returns `true` if docker is installed and running, `false` otherwise
412
+ */
413
+ async function isDockerRunning() {
414
+ if (!whichSync('docker').location) {
415
+ trace('whichSync will not check if docker is running because docker does not appear to be installed - returning false');
416
+ return false;
417
+ }
418
+ return new Promise((resolve) => {
419
+ (0, node_child_process_1.exec)('docker info', (error, stdout) => {
420
+ if (error) {
421
+ resolve(false);
422
+ }
423
+ else {
424
+ if (!stdout || stdout.includes('error during connect')) {
425
+ resolve(false);
426
+ }
427
+ else {
428
+ resolve(true);
429
+ }
430
+ }
431
+ });
432
+ });
433
+ }
434
+ exports.isDockerRunning = isDockerRunning;
435
+ /**
436
+ * Uses built-in NodeJS readline to ask a question and return the user's answer.
437
+ * @param query The question to ask
438
+ * @returns A Promise that resolves to the user's answer
439
+ */
440
+ function askQuestion(query) {
441
+ const rl = readline.createInterface({
442
+ input: process.stdin,
443
+ output: process.stdout,
444
+ });
445
+ return new Promise(resolve => rl.question(`\n${query}\n`, ans => {
446
+ rl.close();
447
+ resolve(ans);
448
+ }));
449
+ }
450
+ exports.askQuestion = askQuestion;
451
+ /**
452
+ * A simple CLI prompt using the built-in NodeJS readline functionality to ask for confirmation.
453
+ * @param question The question to ask
454
+ * @returns A Promise that resolves to true if the user answers 'y' or 'yes', false otherwise
455
+ */
456
+ function getConfirmation(question) {
457
+ const rl = readline.createInterface({
458
+ input: process.stdin,
459
+ output: process.stdout,
460
+ });
461
+ return new Promise((resolve) => {
462
+ rl.question(`\n ❓ ${question}\n ➡️ Proceed? (yes/no): `, (answer) => {
463
+ rl.close();
464
+ const confirmed = answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
465
+ log(confirmed ? ' ✅ Proceeding\n' : ' ❌ Aborting\n');
466
+ resolve(confirmed);
467
+ });
468
+ });
469
+ }
470
+ exports.getConfirmation = getConfirmation;
471
+ /**
472
+ * Example of using {@link getConfirmation}.
473
+ */
474
+ async function getConfirmationExample() {
475
+ if (await getConfirmation('Do you even?')) {
476
+ log('you do even');
477
+ }
478
+ else {
479
+ log('you do not even');
480
+ }
481
+ }
482
+ exports.getConfirmationExample = getConfirmationExample;
483
+ /**
484
+ * Copy entries from a source .env file to a destination .env file for which the destination .env file does not already have entries.
485
+ * If the destination .env file does not exist, it will be created and populated with the source .env file's values.
486
+ *
487
+ * This is useful for copying values from a .env.template file to a root .env file.
488
+ *
489
+ * For copying root .env files to other locations, use {@link overwriteEnvFile}.
490
+ * @param sourcePath The path to the source .env file such as a `.env.template` file (use {@link overwriteEnvFile} for copying root .env files to other locations)
491
+ * @param destinationPath The path to the destination .env file, such as the root .env file
492
+ */
493
+ async function copyNewEnvValues(sourcePath, destinationPath) {
494
+ await (0, generalUtilsInternal_js_1.copyEnv)(sourcePath, destinationPath, false);
495
+ }
496
+ exports.copyNewEnvValues = copyNewEnvValues;
497
+ /**
498
+ * Copy entries from a source .env file to a destination .env file, overwriting any existing entries in the destination .env file.
499
+ * If the destination .env file does not exist, it will be created and populated with the source .env file's values.
500
+ *
501
+ * This is useful for copying values from a root .env file to additional locations (server, client, docker-compose directory, etc.)
502
+ * throughout your solution so you only have to manage one .env file.
503
+ *
504
+ * Note that this does not delete any existing entries in the destination .env file, which is useful if you have additional entries in
505
+ * the destination .env file that you don't want to overwrite.
506
+ *
507
+ * For copying .env.template files to root .env files, use {@link copyNewEnvValues}.
508
+ * @param sourcePath The path to the source .env file such as a root .env file (use {@link copyNewEnvValues} for .env.template files)
509
+ * @param destinationPath The path to the destination .env file
510
+ * @param suppressAddKeysMessages If true, messages about adding missing keys will not be logged (useful if you're always calling {@link copyModifiedEnv} after this call)
511
+ */
512
+ async function overwriteEnvFile(sourcePath, destinationPath, suppressAddKeysMessages = false) {
513
+ await (0, generalUtilsInternal_js_1.copyEnv)(sourcePath, destinationPath, true, suppressAddKeysMessages);
514
+ }
515
+ exports.overwriteEnvFile = overwriteEnvFile;
516
+ /**
517
+ * Copy entries from a source .env file to a destination .env file, but only for the keys specified in keepKeys.
518
+ * Will also modify entries in the destination .env file as specified in modifyEntries.
519
+ * @param sourcePath The path to the source .env file
520
+ * @param destinationPath The path to the destination .env file
521
+ * @param keepKeys The keys to keep from the source .env file
522
+ * @param modifyEntries The entries to modify in the destination .env file
523
+ */
524
+ async function copyModifiedEnv(sourcePath, destinationPath, keepKeys, modifyEntries) {
525
+ requireValidPath('sourcePath', sourcePath);
526
+ const destPathDir = node_path_1.default.dirname(destinationPath);
527
+ if (!node_fs_1.default.existsSync(destPathDir)) {
528
+ await ensureDirectory(destPathDir);
529
+ }
530
+ const sourceDict = (0, generalUtilsInternal_js_1.getEnvAsDictionary)(sourcePath);
531
+ const newDict = filterDictionary(sourceDict, key => keepKeys.includes(key));
532
+ if (modifyEntries && Object.keys(modifyEntries).length > 0) {
533
+ for (const [key, value] of Object.entries(modifyEntries)) {
534
+ newDict[key] = value;
535
+ }
536
+ }
537
+ const newSortedDict = sortDictionaryByKeyAsc(newDict);
538
+ const newEnvFileContent = (0, generalUtilsInternal_js_1.dictionaryToEnvFileString)(newSortedDict);
539
+ await promises_1.default.writeFile(destinationPath, newEnvFileContent);
540
+ }
541
+ exports.copyModifiedEnv = copyModifiedEnv;
542
+ /**
543
+ * Filters a dictionary by key.
544
+ * @param dict The dictionary to filter
545
+ * @param predicate A function that returns true if the key should be included in the filtered dictionary
546
+ * @returns A new dictionary with only the keys that passed the predicate
547
+ */
548
+ function filterDictionary(dict, predicate) {
549
+ // Notes to self:
550
+ // - The second param of reduce is the initial value of the accumulator
551
+ // - Reduce processes each element of the array and returns the accumulator for the next iteration
552
+ // - In our case, the accumulator is a new dictionary that we're building up
553
+ return Object.keys(dict)
554
+ .filter(predicate)
555
+ .reduce((accumulator, key) => {
556
+ accumulator[key] = dict[key];
557
+ return accumulator;
558
+ }, {});
559
+ }
560
+ exports.filterDictionary = filterDictionary;
561
+ /**
562
+ * Sorts a dictionary by key in ascending order.
563
+ * @param dict The dictionary to sort
564
+ * @returns A new dictionary sorted by key in ascending order
565
+ */
566
+ function sortDictionaryByKeyAsc(dict) {
567
+ const newSortedDict = Object.entries(dict).sort((a, b) => {
568
+ if (a < b) {
569
+ return -1;
570
+ }
571
+ if (a > b) {
572
+ return 1;
573
+ }
574
+ return 0;
575
+ });
576
+ return Object.fromEntries(newSortedDict);
577
+ }
578
+ exports.sortDictionaryByKeyAsc = sortDictionaryByKeyAsc;
579
+ /**
580
+ * Helper method to delete a .env file if it exists.
581
+ * @param envPath The path to the .env file to delete
582
+ */
583
+ async function deleteEnvIfExists(envPath) {
584
+ // Just protecting ourselves from accidentally deleting something we didn't mean to
585
+ if (envPath.endsWith('.env') === false) {
586
+ throw new Error(`envPath must end with '.env': ${envPath}`);
587
+ }
588
+ // Using fsp.unlink will throw an error if it's a directory
589
+ if (node_fs_1.default.existsSync(envPath)) {
590
+ await promises_1.default.unlink(envPath);
591
+ }
592
+ }
593
+ exports.deleteEnvIfExists = deleteEnvIfExists;
594
+ /**
595
+ * Searches a directory recursively for files that match the specified pattern.
596
+ * The filenamePattern is a simple text string with asterisks (*) for wildcards.
597
+ * @param dir The directory to find files in
598
+ * @param filenamePattern The pattern to match files against
599
+ * @param options Specify a max depth to search, defaults to 5
600
+ * @returns A Promise that resolves to an array of file paths that match the pattern
601
+ */
602
+ async function findFilesRecursively(dir, filenamePattern, options) {
603
+ requireValidPath('dir', dir);
604
+ requireString('pattern', filenamePattern);
605
+ if (filenamePattern.length > 50) {
606
+ throw new Error(`filenamePattern param must have fewer than 50 characters`);
607
+ }
608
+ const numWildcards = filenamePattern.replace(/\*+/g, '*').split('*').length - 1;
609
+ if (numWildcards > 5) {
610
+ throw new Error(`filenamePattern param must contain 5 or fewer wildcards`);
611
+ }
612
+ if (filenamePattern.includes('/') || filenamePattern.includes('\\')) {
613
+ throw new Error('filenamePattern param must not contain slashes');
614
+ }
615
+ const defaultOptions = { maxDepth: 5 };
616
+ const mergedOptions = { ...defaultOptions, ...options };
617
+ // Convert the pattern to a regex
618
+ const regex = new RegExp('^' + filenamePattern.split(/\*+/).map(escapeStringForRegex).join('.*') + '$');
619
+ const matches = [];
620
+ // Recursive function to search within directories
621
+ async function searchDirectory(directory, depth) {
622
+ if (depth > mergedOptions.maxDepth)
623
+ return;
624
+ const entries = await promises_1.default.readdir(directory, { withFileTypes: true });
625
+ for (const entry of entries) {
626
+ const fullPath = (0, node_path_1.resolve)(directory, entry.name);
627
+ if (entry.isDirectory()) {
628
+ // Check if directory is in the exclude list
629
+ if (!mergedOptions.excludeDirectoryNames || !mergedOptions.excludeDirectoryNames.includes(entry.name)) {
630
+ await searchDirectory(fullPath, depth + 1);
631
+ }
632
+ }
633
+ else if (entry.isFile() && regex.test(entry.name)) {
634
+ if (mergedOptions.returnForwardSlashRelativePaths) {
635
+ matches.push(node_path_1.default.relative(dir, fullPath).replace(/\\/g, '/'));
636
+ }
637
+ else {
638
+ matches.push(fullPath);
639
+ }
640
+ }
641
+ }
642
+ }
643
+ await searchDirectory(dir, 1); // Start search from the first depth
644
+ return matches;
645
+ }
646
+ exports.findFilesRecursively = findFilesRecursively;
647
+ /** Utility function to escape a string for use within regex */
648
+ function escapeStringForRegex(str) {
649
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
650
+ }
651
+ exports.escapeStringForRegex = escapeStringForRegex;
652
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2VuZXJhbFV0aWxzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2dlbmVyYWxVdGlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLDJEQUFrRTtBQUNsRSxzREFBd0I7QUFDeEIsZ0VBQWtDO0FBQ2xDLHFDQUFrQztBQUNsQyx1REFBeUM7QUFDekMsbURBQW9DO0FBQ3BDLG1FQUFnRDtBQUNoRCx1RUFBNEk7QUFFNUksTUFBTSx3Q0FBd0MsR0FBRyxDQUFDLE1BQU0sRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxJQUFJLENBQUMsQ0FBQTtBQUVoSDs7OztHQUlHO0FBQ0gsU0FBZ0IsR0FBRyxDQUFDLElBQWEsRUFBRSxHQUFHLFFBQW1CO0lBQ3ZELE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLEdBQUcsUUFBUSxDQUFDLENBQUE7QUFDaEMsQ0FBQztBQUZELGtCQUVDO0FBRUQ7Ozs7R0FJRztBQUNILFNBQWdCLEtBQUssQ0FBQyxJQUFjLEVBQUUsR0FBRyxRQUFtQjtJQUMxRCxJQUFJLDhCQUFNLENBQUMsWUFBWSxFQUFFO1FBQ3ZCLE1BQU0sTUFBTSxHQUFHLFNBQVMsQ0FBQTtRQUN4QixPQUFPLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxJQUFJLEVBQUUsR0FBRyxRQUFRLENBQUMsQ0FBQTtLQUN2QztBQUNILENBQUM7QUFMRCxzQkFLQztBQWlDRDs7OztHQUlHO0FBQ0gsTUFBYSxVQUFXLFNBQVEsS0FBSztJQUduQyxZQUFZLE9BQWUsRUFBRSxNQUFtQjtRQUM5QyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUE7UUFDZCxJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQTtJQUN0QixDQUFDO0NBQ0Y7QUFQRCxnQ0FPQztBQVdEOzs7O0dBSUc7QUFDSCxNQUFhLGdCQUFpQixTQUFR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