juisy 2.0.0-beta.9 → 2.0.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 (30) hide show
  1. package/LICENSE +661 -0
  2. package/bin/cli/cmds/index.js +1 -3
  3. package/bin/cli/cmds/public/print-globals.js +1 -1
  4. package/bin/cli/index.js +1 -1
  5. package/dist/cli/CLIFactory.d.ts +2 -0
  6. package/dist/cli/Command.d.ts +46 -6
  7. package/dist/cli/GlobalSettings.schema.json +1 -1
  8. package/dist/cli/InterfaceUtils.d.ts +17 -13
  9. package/dist/cli/OutputUtils.d.ts +1 -12
  10. package/dist/cli/index.d.ts +1 -1
  11. package/dist/cli/index.js +504 -456
  12. package/dist/cli/plugins/register-bump-version-command/cmds/index.d.ts +14 -2
  13. package/dist/cli/plugins/register-lint-commands/augment.d.ts +2 -3
  14. package/dist/cli/plugins/register-lint-commands/settings.d.ts +1 -1
  15. package/dist/cli/plugins/register-release-command/augment.d.ts +2 -2
  16. package/dist/cli/types.d.ts +6 -2
  17. package/dist/cli/utils.d.ts +1 -10
  18. package/dist/eject.d.ts +2 -0
  19. package/dist/index.js +8 -3
  20. package/dist/project-globals.d.ts +7 -26
  21. package/dist/templater/index.js +2 -2
  22. package/dist/vite/plugins/inject-css-variables/index.d.ts +23 -0
  23. package/dist/vite/plugins/inject-css-variables/index.js +60 -0
  24. package/dist/vite/plugins/inject-project-globals/index.d.ts +19 -0
  25. package/dist/vite/plugins/inject-project-globals/index.js +58 -0
  26. package/package.json +198 -190
  27. package/bin/cli/cmds/public/squeeze.js +0 -269
  28. package/dist/cli/plugins/command-handler-injections/augment.d.ts +0 -29
  29. package/dist/cli/plugins/command-handler-injections/command-visitors/command-handler-injections.d.ts +0 -10
  30. package/dist/cli/plugins/command-handler-injections/index.d.ts +0 -3
package/dist/cli/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * juisy v2.0.0-beta.9
2
+ * juisy v2.0.1
3
3
  * Copyright © 2022-Present Hervé Perchec
4
4
  */
5
5
 
@@ -11,19 +11,19 @@ import Yargs from 'yargs/yargs';
11
11
  import { findUpSync } from 'find-up';
12
12
  import { importSingleTs } from 'import-single-ts';
13
13
  import lodashGet from 'lodash.get';
14
- import { execa } from 'execa';
15
- import _prompts from 'prompts';
16
14
  import chalk from 'chalk';
17
15
  import indent from 'indent-string';
18
16
  import _stripAnsi from 'strip-ansi';
17
+ import { execa } from 'execa';
18
+ import _prompts from 'prompts';
19
19
  import dotenv from '@dotenvx/dotenvx';
20
20
  import semver from 'semver';
21
- import { getPackageInfo } from 'juisy';
21
+ import fs$1 from 'node:fs';
22
+ import path from 'node:path';
22
23
  import kebabcase from 'lodash.kebabcase';
23
24
  import { ReadmeTemplater } from 'juisy/templater';
24
- import path from 'node:path';
25
+ import { getPackageInfo } from 'juisy';
25
26
  import { ESLint } from 'eslint';
26
- import fs$1 from 'node:fs';
27
27
  import yargsParser from 'yargs-parser';
28
28
 
29
29
  class Plugin {
@@ -56,6 +56,285 @@ class Plugin {
56
56
  created;
57
57
  }
58
58
 
59
+ const loading = {
60
+ // Duration of each character displaying (in ms)
61
+ velocity: 250,
62
+ // Reference to call setInterval and clearInterval
63
+ intervalRef: null,
64
+ // Display loading to STDOUT
65
+ display: function(message) {
66
+ const P = ["\\", "|", "/", "-"];
67
+ const S = [" ", ". ", ".. ", "..."];
68
+ let x = 0;
69
+ let y = 0;
70
+ const velocity = this.velocity;
71
+ this.intervalRef = setInterval(function() {
72
+ process.stdout.write("\r" + P[x++] + " " + message + " : " + S[y++]);
73
+ x &= 3;
74
+ y &= 3;
75
+ }, velocity);
76
+ },
77
+ stop: function() {
78
+ clearInterval(this.intervalRef);
79
+ try {
80
+ process.stdout.clearLine(0);
81
+ process.stdout.cursorTo(0);
82
+ } catch (err) {
83
+ console.log("Warn: process.stdout.clearLine (or cursorTo) function is non-TTY. Skipped...");
84
+ }
85
+ }
86
+ };
87
+ class OutputUtils {
88
+ /**
89
+ * To automatically increment step index
90
+ * @ignore
91
+ */
92
+ static stepsCache = {
93
+ index: 0
94
+ // 0 by default
95
+ };
96
+ /**
97
+ * Use `chalk` package to style output in console/stdout
98
+ * @example
99
+ * const { $style } = OutputUtils
100
+ * console.log($style.green('Green text!')) // => '[32mGreen text![0m'
101
+ */
102
+ static $style = chalk;
103
+ /**
104
+ * Format a message for console output
105
+ * @param msg - The message to format
106
+ * @param options - Format options
107
+ * @returns The formatted message
108
+ * @example
109
+ * formatOutputMessage('laundry\nshopping', { indentChar: '- ' })
110
+ * // => '- laundry\n- shopping'
111
+ *
112
+ * formatOutputMessage('foo\nbar', { indent: 2, indentChar: '❤' })
113
+ * // => '❤❤foo\n❤❤bar'
114
+ */
115
+ static formatOutputMessage(msg, options = {}) {
116
+ let formatted = `${msg}`;
117
+ if (options.indentChar) {
118
+ if (options.indent === void 0) {
119
+ options.indent = 1;
120
+ }
121
+ } else {
122
+ options.indentChar = " ";
123
+ options.indent = options.indent || 0;
124
+ }
125
+ if (options.indent) {
126
+ formatted = indent(msg, options.indent, { indent: options.indentChar });
127
+ }
128
+ return formatted;
129
+ }
130
+ /**
131
+ * @param msg - The message
132
+ * @param options - (Optional) Options object
133
+ * @param options.indent - How many repeat indent (see `indent-string` package)
134
+ * @param options.indentChar - Indent character(s). If `options.indent` not provided, will be repeated only 1 time
135
+ * @description
136
+ * Log message in console
137
+ * @example
138
+ * const { log } = OutputUtils
139
+ * log() // => blank line
140
+ * log('Hello world! =)') // => 'Hello world! =)'
141
+ * log('To do:', { indent: 2 }) // => ' To do:'
142
+ */
143
+ static log(msg, options = {}) {
144
+ const loggerInstance = options.loggerInstance || console;
145
+ if (msg) {
146
+ loggerInstance.log(OutputUtils.formatOutputMessage(msg, options));
147
+ } else {
148
+ loggerInstance.log();
149
+ }
150
+ }
151
+ /**
152
+ * Display warning message
153
+ * @param msg - The message to display
154
+ * @example
155
+ * const { warn } = OutputUtils
156
+ * warn('No configuration file') // => '[33m⚠ Warning: No configuration file[0m'
157
+ */
158
+ static warn(msg) {
159
+ OutputUtils.log(OutputUtils.$style.yellow(`⚠ Warning: ${msg}`));
160
+ }
161
+ /**
162
+ * @param msg - The message to display
163
+ * @param err - If provided, throw the error
164
+ * @description
165
+ * Display error message. Throws `err` if provided.
166
+ * @example
167
+ * const { error } = require('@hperchec/juisy').utils
168
+ * error('No configuration file') // => '[31m⨉ ERROR: No configuration file[0m'
169
+ * error('No configuration file', error) // => Log and throws error
170
+ */
171
+ static error(msg, err) {
172
+ OutputUtils.log(OutputUtils.$style.red(`⨉ ERROR: ${msg}`));
173
+ if (err !== void 0) {
174
+ OutputUtils.log();
175
+ throw err;
176
+ }
177
+ }
178
+ /**
179
+ * Log step title
180
+ * @param msg - The message to display
181
+ * @param options - Options object
182
+ * @param options.index - Custom index. If `null`, no index prepended to string
183
+ * @example
184
+ * step('Important step') // => '● 1 - Important step'
185
+ * step('Very important step') // => '● 2 - Very important step'
186
+ */
187
+ static step(msg, options = {}) {
188
+ OutputUtils.stepsCache.index++;
189
+ if (options.index === void 0) {
190
+ options.index = OutputUtils.stepsCache.index;
191
+ }
192
+ OutputUtils.log(`${options.index !== null ? options.index + " - " : ""}${msg}`, { indentChar: "● " });
193
+ }
194
+ /**
195
+ * Log substep title
196
+ * @param msg - The message to display
197
+ * @param options - Options object
198
+ * @param options.last - Defines if it is the last substep
199
+ * @example
200
+ * const { substep } = require('@hperchec/juisy').utils
201
+ * substep('Awesome substep') // => '├ Awesome substep'
202
+ * substep('Last substep', { last: true }) // => '└ Last substep'
203
+ */
204
+ static substep(msg, options = {}) {
205
+ OutputUtils.log(msg, { indentChar: options.last ? "└ " : "├ " });
206
+ }
207
+ /**
208
+ * @param {string} message - Message to display
209
+ * @param {Function} fct - The function to execute
210
+ * @returns {Promise<void>}
211
+ * @description
212
+ * Wait function: display loading during 'fct' execution
213
+ * @example
214
+ * const { wait } = require('@hperchec/juisy').utils
215
+ * await wait('Waiting', async () => {
216
+ * // Do async logic
217
+ * })
218
+ */
219
+ static async wait(message, fct) {
220
+ loading.display(message);
221
+ await fct();
222
+ loading.stop();
223
+ }
224
+ /**
225
+ * See `strip-ansi` package documentation
226
+ * @see https://www.npmjs.com/package/strip-ansi
227
+ */
228
+ static stripAnsi = _stripAnsi;
229
+ }
230
+
231
+ class Command {
232
+ /**
233
+ * Creates a Command instance from object (CommandObject)
234
+ * @param {CommandObject} commandObject - The command definition object
235
+ */
236
+ constructor(commandObject) {
237
+ if (commandObject) {
238
+ this.command = commandObject.command;
239
+ this.aliases = commandObject.aliases;
240
+ this.describe = commandObject.describe;
241
+ this.deprecated = commandObject.deprecated;
242
+ this.meta = commandObject.meta;
243
+ if (commandObject.builder)
244
+ this.builder = commandObject.builder;
245
+ if (commandObject.handler)
246
+ this.handler = commandObject.handler;
247
+ this.middlewares = commandObject.middlewares;
248
+ }
249
+ }
250
+ /**
251
+ * The command
252
+ */
253
+ command;
254
+ /**
255
+ * The aliases
256
+ */
257
+ aliases;
258
+ /**
259
+ * Command description
260
+ */
261
+ describe;
262
+ /**
263
+ * Is deprecated?
264
+ */
265
+ deprecated;
266
+ /**
267
+ * Command meta
268
+ */
269
+ meta;
270
+ /**
271
+ * Command builder
272
+ */
273
+ builder(cli) {
274
+ return cli;
275
+ }
276
+ /**
277
+ * Command handler
278
+ */
279
+ handler(argv) {
280
+ throw new Error("Command handler is not implemented");
281
+ }
282
+ /**
283
+ * Command middlewares
284
+ */
285
+ middlewares;
286
+ /**
287
+ * Log a message in the console output. Uses OutputUtils.log method.
288
+ * @param msg The message to log. Can be empty to log blank line
289
+ * @param options The options to pass to OutputUtils.log second argument
290
+ */
291
+ log(msg, options = {}) {
292
+ OutputUtils.log(msg, {
293
+ ...options,
294
+ loggerInstance: this.constructor.engine.getInternalMethods().getLoggerInstance()
295
+ });
296
+ }
297
+ /**
298
+ * Get the process.argv without bin
299
+ * @returns An array of command line arguments
300
+ */
301
+ getProcessArgv() {
302
+ return hideBin(process.argv);
303
+ }
304
+ /**
305
+ * Get Command instance as plain object
306
+ * @returns The command definition object to pass to yargs
307
+ */
308
+ toObject() {
309
+ return {
310
+ command: this.command,
311
+ aliases: this.aliases,
312
+ describe: this.describe,
313
+ deprecated: this.deprecated,
314
+ meta: this.meta,
315
+ builder: this.builder,
316
+ handler: this.handler.bind(this),
317
+ middlewares: this.middlewares
318
+ };
319
+ }
320
+ /**
321
+ * Getters
322
+ */
323
+ /**
324
+ * Get the CLI engine
325
+ */
326
+ get engine() {
327
+ return this.constructor.engine;
328
+ }
329
+ /**
330
+ * Static properties
331
+ */
332
+ /**
333
+ * The CLI engine. Will be set by createEngine method
334
+ */
335
+ static engine;
336
+ }
337
+
59
338
  function createEngine(yargs) {
60
339
  const xargs = yargs;
61
340
  xargs._isPrivate = false;
@@ -78,7 +357,8 @@ function createEngine(yargs) {
78
357
  xargs.enableGlobalCommandVisitors = enableGlobalCommandVisitors;
79
358
  xargs.isPrivate = isPrivate;
80
359
  xargs.getSettings = getSettings;
81
- return xargs.strict().help();
360
+ Command.engine = xargs.strict().help();
361
+ return Command.engine;
82
362
  }
83
363
  function command(...args) {
84
364
  const self = this;
@@ -96,10 +376,13 @@ function command(...args) {
96
376
  }
97
377
  return failOnFalsyReturn ? false : cmdObj;
98
378
  };
379
+ const isClass = (value) => {
380
+ return typeof value === "function" && value.toString().startsWith("class ");
381
+ };
99
382
  if (args[0] instanceof Array) {
100
383
  const cmdsArray = [];
101
384
  for (const commandModule of args[0]) {
102
- const visited = visit(commandModule);
385
+ const visited = visit((isClass(commandModule) ? new commandModule() : commandModule).toObject());
103
386
  if (visited)
104
387
  cmdsArray.push(visited);
105
388
  }
@@ -198,365 +481,152 @@ const findCommandIn = (fullSignature, commands) => {
198
481
  if (command.children?.length) {
199
482
  const found = findCommandIn(fullSignature, command.children);
200
483
  if (found)
201
- return found;
202
- }
203
- }
204
- }
205
- };
206
- const getDefaultCommand = (fullSignature) => {
207
- return findCommandIn(fullSignature, defaultCommands);
208
- };
209
- const resolveSettingsCallbacks = {
210
- // ...
211
- };
212
- function defineSettings(target, settings, options) {
213
- if (options?.resolve) {
214
- resolveSettingsCallbacks[target] = options.resolve;
215
- }
216
- defaultGlobalSettings[target] = settings;
217
- }
218
- async function CLIFactory(builder, options) {
219
- let settingsPath;
220
- let globalSettings;
221
- for (const plugin of registeredPlugins) {
222
- await plugin.beforeCreate({
223
- defineDefaultCommand,
224
- defineSettings,
225
- builder,
226
- factoryOptions: options || {}
227
- });
228
- }
229
- if (options?.settingsPath) {
230
- settingsPath = options.settingsPath;
231
- }
232
- const configPath = findUpSync(settingsPath ? [settingsPath] : [
233
- "dev.config.ts",
234
- "dev.config.js",
235
- "dev.config.json",
236
- "cli.config.ts",
237
- "cli.config.js",
238
- "cli.config.json",
239
- ".clirc.ts",
240
- ".clirc.js",
241
- ".clirc.json"
242
- ]);
243
- if (settingsPath && !configPath) {
244
- throw new Error("Unable to find file: " + settingsPath);
245
- } else if (configPath) {
246
- if (configPath.endsWith(".ts")) {
247
- globalSettings = deepmerge(defaultGlobalSettings, await importSingleTs(configPath).then((mod) => mod.default || mod));
248
- } else if (configPath.endsWith(".json")) {
249
- globalSettings = deepmerge(defaultGlobalSettings, JSON.parse(fs.readFileSync(configPath).toString()));
250
- } else {
251
- globalSettings = deepmerge(defaultGlobalSettings, await import(pathToFileURL(configPath).toString()).then((mod) => mod.default || mod));
252
- }
253
- } else {
254
- globalSettings = defaultGlobalSettings;
255
- }
256
- const cli = function(argv) {
257
- let engine = createEngine(Yargs(argv));
258
- engine._settings = proxify(globalSettings);
259
- for (const plugin of registeredPlugins) {
260
- plugin.created({
261
- engine,
262
- builder,
263
- factoryOptions: options || {}
264
- });
265
- }
266
- engine = builder(engine);
267
- const rootHandlers = engine.getInternalMethods().getCommandInstance().handlers;
268
- for (const key in rootHandlers) {
269
- const handler = rootHandlers[key];
270
- if (defaultCommands.find((cmdObj) => cmdObj.fullSignature === handler.original)) {
271
- engine.disableDefaultCommand(handler.original);
272
- }
273
- }
274
- for (const defaultCommand of defaultCommands) {
275
- if (engine.hasDefaultCommand(defaultCommand.fullSignature)) {
276
- engine.command({
277
- // @ts-ignore
278
- aliases: defaultCommand.commandObject.aliases,
279
- command: defaultCommand.commandObject.command,
280
- describe: defaultCommand.commandObject.describe,
281
- builder: (cli2) => {
282
- return defaultCommand.commandObject.builder(cli2);
283
- },
284
- handler: defaultCommand.commandObject.handler,
285
- meta: defaultCommand.commandObject.meta,
286
- middlewares: defaultCommand.commandObject.middlewares
287
- });
288
- }
289
- }
290
- return engine;
291
- };
292
- return cli;
293
- }
294
- CLIFactory.use = function(plugin) {
295
- if (plugin instanceof Plugin) {
296
- if (registeredPlugins.find((rp) => rp.name === plugin.name)) {
297
- throw new Error(`CLI plugin "${plugin.name}" is already used`);
298
- } else {
299
- registeredPlugins.push(plugin);
300
- }
301
- } else {
302
- throw new Error(`Provided plugin is not an instance of Plugin`);
303
- }
304
- };
305
-
306
- class Command {
307
- /**
308
- * Creates a Command instance from object (CommandObject)
309
- * @param {CommandObject} commandObject - The command definition object
310
- */
311
- constructor(commandObject) {
312
- this.command = commandObject.command;
313
- this.aliases = commandObject.aliases;
314
- this.describe = commandObject.describe;
315
- this.deprecated = commandObject.deprecated;
316
- this.meta = commandObject.meta;
317
- this.builder = commandObject.builder || ((cli) => cli);
318
- this.handler = commandObject.handler;
319
- this.middlewares = commandObject.middlewares;
320
- }
321
- /**
322
- * The command
323
- */
324
- command;
325
- /**
326
- * The aliases
327
- */
328
- aliases;
329
- /**
330
- * Command description
331
- */
332
- describe;
333
- /**
334
- * Is deprecated?
335
- */
336
- deprecated;
337
- /**
338
- * Command meta
339
- */
340
- meta;
341
- /**
342
- * Command builder
343
- */
344
- builder;
345
- /**
346
- * Command handler
347
- */
348
- // @ts-ignore
349
- handler;
350
- /**
351
- * Command middlewares
352
- */
353
- middlewares;
354
- }
355
-
356
- const loading = {
357
- // Duration of each character displaying (in ms)
358
- velocity: 250,
359
- // Reference to call setInterval and clearInterval
360
- intervalRef: null,
361
- // Display loading to STDOUT
362
- display: function(message) {
363
- const P = ["\\", "|", "/", "-"];
364
- const S = [" ", ". ", ".. ", "..."];
365
- let x = 0;
366
- let y = 0;
367
- const velocity = this.velocity;
368
- this.intervalRef = setInterval(function() {
369
- process.stdout.write("\r" + P[x++] + " " + message + " : " + S[y++]);
370
- x &= 3;
371
- y &= 3;
372
- }, velocity);
373
- },
374
- stop: function() {
375
- clearInterval(this.intervalRef);
376
- try {
377
- process.stdout.clearLine(0);
378
- process.stdout.cursorTo(0);
379
- } catch (err) {
380
- console.log("Warn: process.stdout.clearLine (or cursorTo) function is non-TTY. Skipped...");
381
- }
382
- }
383
- };
384
- class OutputUtils {
385
- /**
386
- * To automatically increment step index
387
- * @ignore
388
- */
389
- static stepsCache = {
390
- index: 0
391
- // 0 by default
392
- };
393
- /**
394
- * Use `chalk` package to style output in console/stdout
395
- * @example
396
- * const { $style } = OutputUtils
397
- * console.log($style.green('Green text!')) // => '[32mGreen text![0m'
398
- */
399
- static $style = chalk;
400
- /**
401
- * Format a message for console output
402
- * @param msg - The message to format
403
- * @param options - Format options
404
- * @returns The formatted message
405
- * @example
406
- * formatOutputMessage('laundry\nshopping', { indentChar: '- ' })
407
- * // => '- laundry\n- shopping'
408
- *
409
- * formatOutputMessage('foo\nbar', { indent: 2, indentChar: '❤' })
410
- * // => '❤❤foo\n❤❤bar'
411
- */
412
- static formatOutputMessage(msg, options = {}) {
413
- let formatted = `${msg}`;
414
- if (options.indentChar) {
415
- if (options.indent === void 0) {
416
- options.indent = 1;
484
+ return found;
417
485
  }
418
- } else {
419
- options.indentChar = " ";
420
- options.indent = options.indent || 0;
421
486
  }
422
- if (options.indent) {
423
- formatted = indent(msg, options.indent, { indent: options.indentChar });
424
- }
425
- return formatted;
426
487
  }
427
- /**
428
- * @param msg - The message
429
- * @param options - (Optional) Options object
430
- * @param options.indent - How many repeat indent (see `indent-string` package)
431
- * @param options.indentChar - Indent character(s). If `options.indent` not provided, will be repeated only 1 time
432
- * @description
433
- * Log message in console
434
- * @example
435
- * const { log } = OutputUtils
436
- * log() // => blank line
437
- * log('Hello world! =)') // => 'Hello world! =)'
438
- * log('To do:', { indent: 2 }) // => ' To do:'
439
- */
440
- static log(msg, options = {}) {
441
- const loggerInstance = options.loggerInstance || console;
442
- if (msg) {
443
- loggerInstance.log(OutputUtils.formatOutputMessage(msg, options));
444
- } else {
445
- loggerInstance.log();
446
- }
488
+ };
489
+ const getDefaultCommand = (fullSignature) => {
490
+ return findCommandIn(fullSignature, defaultCommands);
491
+ };
492
+ const resolveSettingsCallbacks = {
493
+ // ...
494
+ };
495
+ function defineSettings(target, settings, options) {
496
+ if (options?.resolve) {
497
+ resolveSettingsCallbacks[target] = options.resolve;
447
498
  }
448
- /**
449
- * Display warning message
450
- * @param msg - The message to display
451
- * @example
452
- * const { warn } = OutputUtils
453
- * warn('No configuration file') // => '[33m⚠ Warning: No configuration file[0m'
454
- */
455
- static warn(msg) {
456
- OutputUtils.log(OutputUtils.$style.yellow(`⚠ Warning: ${msg}`));
499
+ defaultGlobalSettings[target] = settings;
500
+ }
501
+ async function CLIFactory(builder, options) {
502
+ let settingsPath;
503
+ let globalSettings;
504
+ for (const plugin of registeredPlugins) {
505
+ await plugin.beforeCreate({
506
+ defineDefaultCommand,
507
+ defineSettings,
508
+ builder,
509
+ factoryOptions: options || {}
510
+ });
457
511
  }
458
- /**
459
- * @param msg - The message to display
460
- * @param err - If provided, throw the error
461
- * @description
462
- * Display error message. Throws `err` if provided.
463
- * @example
464
- * const { error } = require('@hperchec/juisy').utils
465
- * error('No configuration file') // => '[31m⨉ ERROR: No configuration file[0m'
466
- * error('No configuration file', error) // => Log and throws error
467
- */
468
- static error(msg, err) {
469
- OutputUtils.log(OutputUtils.$style.red(`⨉ ERROR: ${msg}`));
470
- if (err !== void 0) {
471
- OutputUtils.log();
472
- throw err;
473
- }
512
+ if (options?.settingsPath) {
513
+ settingsPath = options.settingsPath;
474
514
  }
475
- /**
476
- * Log step title
477
- * @param msg - The message to display
478
- * @param options - Options object
479
- * @param options.index - Custom index. If `null`, no index prepended to string
480
- * @example
481
- * step('Important step') // => '● 1 - Important step'
482
- * step('Very important step') // => '● 2 - Very important step'
483
- */
484
- static step(msg, options = {}) {
485
- OutputUtils.stepsCache.index++;
486
- if (options.index === void 0) {
487
- options.index = OutputUtils.stepsCache.index;
515
+ const configPath = findUpSync(settingsPath ? [settingsPath] : [
516
+ "dev.config.ts",
517
+ "dev.config.js",
518
+ "dev.config.json",
519
+ "cli.config.ts",
520
+ "cli.config.js",
521
+ "cli.config.json",
522
+ ".clirc.ts",
523
+ ".clirc.js",
524
+ ".clirc.json"
525
+ ]);
526
+ if (settingsPath && !configPath) {
527
+ throw new Error("Unable to find file: " + settingsPath);
528
+ } else if (configPath) {
529
+ if (configPath.endsWith(".ts")) {
530
+ globalSettings = deepmerge(defaultGlobalSettings, await importSingleTs(configPath).then((mod) => mod.default || mod));
531
+ } else if (configPath.endsWith(".json")) {
532
+ globalSettings = deepmerge(defaultGlobalSettings, JSON.parse(fs.readFileSync(configPath).toString()));
533
+ } else {
534
+ globalSettings = deepmerge(defaultGlobalSettings, await import(pathToFileURL(configPath).toString()).then((mod) => mod.default || mod));
488
535
  }
489
- OutputUtils.log(`${options.index !== null ? options.index + " - " : ""}${msg}`, { indentChar: "● " });
536
+ } else {
537
+ globalSettings = defaultGlobalSettings;
490
538
  }
491
- /**
492
- * Log substep title
493
- * @param msg - The message to display
494
- * @param options - Options object
495
- * @param options.last - Defines if it is the last substep
496
- * @example
497
- * const { substep } = require('@hperchec/juisy').utils
498
- * substep('Awesome substep') // => '├ Awesome substep'
499
- * substep('Last substep', { last: true }) // => '└ Last substep'
500
- */
501
- static substep(msg, options = {}) {
502
- OutputUtils.log(msg, { indentChar: options.last ? "└ " : "├ " });
539
+ const cli = function(argv) {
540
+ let engine = createEngine(Yargs(argv));
541
+ engine._settings = proxify(globalSettings);
542
+ for (const plugin of registeredPlugins) {
543
+ plugin.created({
544
+ engine,
545
+ builder,
546
+ factoryOptions: options || {}
547
+ });
548
+ }
549
+ engine = builder(engine);
550
+ const rootHandlers = engine.getInternalMethods().getCommandInstance().handlers;
551
+ for (const key in rootHandlers) {
552
+ const handler = rootHandlers[key];
553
+ if (defaultCommands.find((cmdObj) => cmdObj.fullSignature === handler.original)) {
554
+ engine.disableDefaultCommand(handler.original);
555
+ }
556
+ }
557
+ for (const defaultCommand of defaultCommands) {
558
+ if (engine.hasDefaultCommand(defaultCommand.fullSignature)) {
559
+ engine.command(defaultCommand.commandObject.toObject());
560
+ }
561
+ }
562
+ return engine;
563
+ };
564
+ return cli;
565
+ }
566
+ CLIFactory.use = function(plugin) {
567
+ if (plugin instanceof Plugin) {
568
+ if (registeredPlugins.find((rp) => rp.name === plugin.name)) {
569
+ throw new Error(`CLI plugin "${plugin.name}" is already used`);
570
+ } else {
571
+ registeredPlugins.push(plugin);
572
+ }
573
+ } else {
574
+ throw new Error(`Provided plugin is not an instance of Plugin`);
503
575
  }
504
- /**
505
- * @param {string} message - Message to display
506
- * @param {Function} fct - The function to execute
507
- * @returns {Promise<void>}
508
- * @description
509
- * Wait function: display loading during 'fct' execution
510
- * @example
511
- * const { wait } = require('@hperchec/juisy').utils
512
- * await wait('Waiting', async () => {
513
- * // Do async logic
514
- * })
515
- */
516
- static async wait(message, fct) {
517
- loading.display(message);
518
- await fct();
519
- loading.stop();
576
+ };
577
+ CLIFactory.removePlugin = function(name) {
578
+ const index = registeredPlugins.findIndex((rp) => rp.name === name);
579
+ if (index > -1) {
580
+ registeredPlugins.splice(index, 1);
581
+ } else {
582
+ throw new Error(`CLI plugin "${name}" is not registered`);
520
583
  }
521
- /**
522
- * See `strip-ansi` package documentation
523
- * @see https://www.npmjs.com/package/strip-ansi
524
- */
525
- static stripAnsi = _stripAnsi;
526
- }
584
+ };
585
+ CLIFactory.getRegisteredPlugins = function() {
586
+ return [...registeredPlugins];
587
+ };
527
588
 
528
589
  const { log, $style } = OutputUtils;
529
590
  class InterfaceUtils {
530
591
  /**
531
592
  * Get root directory path
532
593
  * @example
533
- * import { CLIUtils: { rootDir } } from '@hperchec/juisy'
594
+ * const { rootDir } = InterfaceUtils
534
595
  * console.log(rootDir) // => 'path/to/your/root/dir'
535
596
  */
536
597
  static rootDir = process.cwd();
537
- /**
538
- * @param {string} bin - Command
539
- * @param {string[]} args - Same as execa second arg
540
- * @param {object} [opts] - Options
541
- * @returns {Promise<object>} The `execa` Promise
542
- * @throws {RunError}
543
- * @description
544
- * Run command (child_process). See also `execa` package documentation
545
- * @example
546
- * const { run } = require('@hperchec/juisy').utils
547
- * await run('npm', [ 'run', 'test' ], { stdio: 'inherit' })
548
- */
549
- static run(bin, args, opts = {}) {
550
- return execa(bin, args, { stdio: "inherit", cwd: InterfaceUtils.rootDir, ...opts });
598
+ static run(bin, args, opts) {
599
+ let execaOptions = {
600
+ stdio: "inherit",
601
+ cwd: InterfaceUtils.rootDir
602
+ };
603
+ let callExeca = () => execa`${bin}`;
604
+ if (args) {
605
+ if (args instanceof Array) {
606
+ execaOptions = {
607
+ ...execaOptions,
608
+ ...opts || {}
609
+ };
610
+ callExeca = () => execa(bin, args, execaOptions);
611
+ } else if (typeof args === "object") {
612
+ execaOptions = {
613
+ ...execaOptions,
614
+ ...args
615
+ };
616
+ callExeca = () => execa(execaOptions)`${bin}`;
617
+ } else {
618
+ throw new Error("Second argument must be either an array of arguments or an object of options");
619
+ }
620
+ }
621
+ return callExeca();
551
622
  }
552
623
  /**
553
- * @alias utils.abort
554
624
  * @param {number} [code] - Code for process.exit() (default: 0)
555
625
  * @returns {void}
556
626
  * @description
557
627
  * Exit process
558
628
  * @example
559
- * const { abort } = require('@hperchec/juisy').utils
629
+ * const { abort } = InterfaceUtils
560
630
  * abort() // => exit process with code 0
561
631
  * abort(1) // => error code
562
632
  */
@@ -565,15 +635,15 @@ class InterfaceUtils {
565
635
  process.exit(code);
566
636
  }
567
637
  /**
568
- * @alias utils.confirm
569
- * @param {prompts.PromptObject} question - A prompt question object (see https://gitlab.com/hperchec/juisy/-/blob/main/documentation/utils.md#utilspromptsargs-object)
638
+ * @param question - A prompt question object
639
+ * @param options - A prompt options object
570
640
  * @returns {Promise<boolean>} - True if confirmed
571
641
  * @description
572
642
  * Demand confirmation with prompts native util. If not confirmed, it will automatically abort the script.
573
643
  * @example
574
644
  * confirm({ message: 'Confirm to continue' }) // Deny it will abort the script
575
645
  */
576
- static async confirm(question) {
646
+ static async confirm(question, options = {}) {
577
647
  if (!question) {
578
648
  question = {
579
649
  type: "confirm",
@@ -588,7 +658,7 @@ class InterfaceUtils {
588
658
  type: "confirm",
589
659
  name: "yes"
590
660
  }
591
- ]);
661
+ ], options);
592
662
  if (!yes) {
593
663
  InterfaceUtils.abort();
594
664
  } else {
@@ -618,79 +688,90 @@ const LoadEnvFile = new Plugin("built-in:load-env-file", {
618
688
  }
619
689
  });
620
690
 
621
- const bumpVersion = new Command({
622
- command: "bump-version",
623
- describe: "Bump version in package.json file",
624
- meta: {
691
+ class BumpVersionCommand extends Command {
692
+ command = "bump-version [targetVersion]";
693
+ describe = "Bump version in package.json file";
694
+ meta = {
625
695
  private: true
626
- },
627
- builder: function(cli) {
696
+ };
697
+ builder(cli) {
698
+ cli.positional("targetVersion", {
699
+ describe: "The target version to set manually",
700
+ type: "string"
701
+ });
628
702
  cli.option("p", {
629
703
  alias: "preid",
630
704
  type: "string",
631
705
  describe: "Pre-release id",
632
706
  requiresArg: true
633
707
  });
708
+ cli.option("f", {
709
+ alias: "file",
710
+ type: "string",
711
+ describe: "Path to package.json file",
712
+ requiresArg: true
713
+ });
714
+ cli.option("indent", {
715
+ type: "number",
716
+ describe: "Number of indent spaces used by JSON.stringify. Default: 2",
717
+ default: 2,
718
+ requiresArg: true
719
+ });
634
720
  return cli;
635
- },
721
+ }
636
722
  async handler(argv) {
637
723
  const { $style, step, substep, error } = OutputUtils;
638
- const { run, prompts, abort } = InterfaceUtils;
639
- const packageJson = getPackageInfo();
640
- let targetVersion;
641
- const currentVersion = packageJson.version;
642
- packageJson.name;
643
- const preId = argv.preid || semver.prerelease(currentVersion) && semver.prerelease(currentVersion)[0];
644
- const inc = (i) => semver.inc(currentVersion, i, preId);
645
- const versionIncrements = [
646
- "patch",
647
- "minor",
648
- "major",
649
- ...preId ? ["prepatch", "preminor", "premajor", "prerelease"] : []
650
- ];
724
+ const { confirm, prompts, abort } = InterfaceUtils;
725
+ const packageJsonPath = argv.file ? path.isAbsolute(argv.file) ? argv.file : path.join(process.cwd(), argv.file) : path.resolve(process.cwd(), "package.json");
726
+ const pkg = JSON.parse(fs$1.readFileSync(packageJsonPath, "utf-8"));
727
+ let targetVersion = argv.targetVersion;
651
728
  step("Bump version");
652
- const { release } = await prompts([
653
- {
654
- type: "select",
655
- name: "release",
656
- message: "Release type:",
657
- choices: versionIncrements.map((i) => ({ title: `${i} (${inc(i)})`, value: inc(i) })).concat([{ title: "custom", value: "custom" }])
658
- }
659
- ]);
660
- if (release === "custom") {
661
- const { version: customVersion } = await prompts([
729
+ if (!targetVersion) {
730
+ const currentVersion = pkg.version;
731
+ pkg.name;
732
+ const preId = argv.preid || semver.prerelease(currentVersion) && semver.prerelease(currentVersion)[0];
733
+ const inc = (i) => semver.inc(currentVersion, i, preId);
734
+ const versionIncrements = [
735
+ "patch",
736
+ "minor",
737
+ "major",
738
+ ...preId ? ["prepatch", "preminor", "premajor", "prerelease"] : []
739
+ ];
740
+ const { release } = await prompts([
662
741
  {
663
- type: "text",
664
- name: "version",
665
- message: "New custom version:",
666
- initial: currentVersion,
667
- validate: (value) => Boolean(semver.valid(value))
742
+ type: "select",
743
+ name: "release",
744
+ message: "Release type:",
745
+ choices: versionIncrements.map((i) => ({ title: `${i} (${inc(i)})`, value: inc(i) })).concat([{ title: "custom", value: "custom" }])
668
746
  }
669
- ]);
670
- targetVersion = customVersion;
671
- } else {
672
- targetVersion = release;
673
- }
674
- const { yes } = await prompts([
675
- {
747
+ ], { onCancel: () => abort() });
748
+ if (release === "custom") {
749
+ const { version: customVersion } = await prompts([
750
+ {
751
+ type: "text",
752
+ name: "version",
753
+ message: "New custom version:",
754
+ initial: currentVersion,
755
+ validate: (value) => Boolean(semver.valid(value))
756
+ }
757
+ ], { onCancel: () => abort() });
758
+ targetVersion = customVersion;
759
+ } else {
760
+ targetVersion = release;
761
+ }
762
+ await confirm({
676
763
  type: "confirm",
677
764
  name: "yes",
678
765
  message: `Releasing v${targetVersion}. Confirm?`,
679
766
  initial: true
680
- }
681
- ]);
682
- if (!yes) {
683
- abort();
684
- return;
767
+ }, {
768
+ onCancel: () => abort()
769
+ });
685
770
  }
686
- this.log();
687
771
  let commandError = false;
688
772
  try {
689
- await run("npm", [
690
- "--no-git-tag-version",
691
- "version",
692
- targetVersion
693
- ], { stdio: "inherit" });
773
+ pkg.version = targetVersion;
774
+ fs$1.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, argv.indent) + "\n");
694
775
  } catch (e) {
695
776
  commandError = e;
696
777
  }
@@ -699,17 +780,18 @@ const bumpVersion = new Command({
699
780
  error("An error has occured.", commandError);
700
781
  abort(1);
701
782
  } else {
702
- substep($style.green("✔ Version successfuly bumped"), { last: true });
783
+ substep($style.green("✔ Version successfuly bumped"));
784
+ substep($style.cyan("New version: " + targetVersion), { last: true });
703
785
  this.log();
704
786
  }
705
787
  }
706
- });
788
+ }
707
789
 
708
790
  const RegisterBumpVersionCommand = new Plugin("built-in:register-bump-version-command", {
709
791
  beforeCreate({ defineDefaultCommand, defineSettings, builder, factoryOptions }) {
710
792
  defineDefaultCommand({
711
793
  fullSignature: "bump-version",
712
- commandObject: bumpVersion,
794
+ commandObject: new BumpVersionCommand(),
713
795
  children: []
714
796
  });
715
797
  }
@@ -822,6 +904,7 @@ const docsGenerateReadme = new Command({
822
904
  alias: "c",
823
905
  describe: "Path to config file",
824
906
  type: "string",
907
+ requiresArg: true,
825
908
  default: "./docs/readme/config.js"
826
909
  });
827
910
  cli.config(cli.getSettings("docs.readme"));
@@ -1082,7 +1165,7 @@ const lintCommit = new Command({
1082
1165
  });
1083
1166
  const lintSettings = cli.getSettings("lint.commit");
1084
1167
  if (lintSettings?.config) {
1085
- cli.config(lintSettings);
1168
+ cli.config({ config: lintSettings.config });
1086
1169
  }
1087
1170
  return cli;
1088
1171
  },
@@ -1261,9 +1344,7 @@ const RegisterLintCommands = new Plugin("built-in:register-lint-commands", {
1261
1344
  config: "eslint.config.js"
1262
1345
  },
1263
1346
  commit: {
1264
- extends: [
1265
- "@commitlint/config-conventional"
1266
- ]
1347
+ // ...
1267
1348
  },
1268
1349
  markdown: [],
1269
1350
  staged: {
@@ -1300,6 +1381,9 @@ const RegisterLintCommands = new Plugin("built-in:register-lint-commands", {
1300
1381
  const release = new Command({
1301
1382
  command: "release",
1302
1383
  describe: "Make a release",
1384
+ meta: {
1385
+ private: true
1386
+ },
1303
1387
  builder(cli) {
1304
1388
  cli.option("d", {
1305
1389
  alias: "dry-run",
@@ -1311,6 +1395,9 @@ const release = new Command({
1311
1395
  describe: 'Increment "major", "minor", "patch", or "pre*" version; or specify version',
1312
1396
  default: "patch",
1313
1397
  requiresArg: true
1398
+ }).option("preRelease", {
1399
+ type: "string",
1400
+ describe: "Same as release-it --preRelease option. See: https://github.com/release-it/release-it/blob/main/docs/pre-releases.md"
1314
1401
  }).option("ci", {
1315
1402
  type: "boolean",
1316
1403
  describe: "No prompts, no user interaction; activated automatically in CI environments"
@@ -1407,6 +1494,10 @@ const release = new Command({
1407
1494
  releaseItCmdArgs.push(argv.increment);
1408
1495
  }
1409
1496
  }
1497
+ if (argv.preRelease !== void 0) {
1498
+ releaseItCmdArgs.push("--preRelease");
1499
+ releaseItCmdArgs.push(argv.preRelease);
1500
+ }
1410
1501
  if (argv.ci) {
1411
1502
  releaseItCmdArgs.push("--ci");
1412
1503
  }
@@ -1470,6 +1561,9 @@ const RegisterReleaseCommand = new Plugin("built-in:register-release-command", {
1470
1561
  const test = new Command({
1471
1562
  command: "test",
1472
1563
  describe: "Run tests",
1564
+ meta: {
1565
+ private: true
1566
+ },
1473
1567
  async handler(argv) {
1474
1568
  const { $style } = OutputUtils;
1475
1569
  this.log($style.yellow("Command not implemented. Read the doc and build your own!"));
@@ -1493,32 +1587,15 @@ function wrapCommandBuilder(target, builder) {
1493
1587
  return _target(builder(cli));
1494
1588
  };
1495
1589
  }
1496
- function wrapCommandhandler(target, handler) {
1497
- return function(args) {
1498
- handler.call(this, args);
1499
- return target.call(this, args);
1500
- };
1501
- }
1502
1590
 
1503
- const visitor$3 = function(commandObject, cli) {
1591
+ const visitor$2 = function(commandObject, cli) {
1504
1592
  const defaultCommand = getDefaultCommand(commandObject.command);
1505
1593
  if (defaultCommand) {
1506
1594
  commandObject.builder = wrapCommandBuilder(commandObject.builder, function(_cli) {
1507
1595
  if (defaultCommand.children?.length) {
1508
1596
  for (const child of defaultCommand.children) {
1509
1597
  if (cli.hasDefaultCommand(child.fullSignature)) {
1510
- cli.command({
1511
- // @ts-ignore
1512
- aliases: child.commandObject.aliases,
1513
- command: child.commandObject.command,
1514
- describe: child.commandObject.describe,
1515
- builder: (__cli) => {
1516
- return child.commandObject.builder(__cli);
1517
- },
1518
- handler: child.commandObject.handler,
1519
- meta: child.commandObject.meta,
1520
- middlewares: child.commandObject.middlewares
1521
- });
1598
+ cli.command(child.commandObject.toObject());
1522
1599
  }
1523
1600
  }
1524
1601
  }
@@ -1527,39 +1604,11 @@ const visitor$3 = function(commandObject, cli) {
1527
1604
  }
1528
1605
  return commandObject;
1529
1606
  };
1530
- const options$3 = {};
1531
-
1532
- const DefaultCommandFallbacks = new Plugin("built-in:default-command-fallbacks", {
1533
- created({ engine, builder, factoryOptions }) {
1534
- engine.globalCommandVisitor("default-command-fallbacks-visitor", visitor$3, options$3);
1535
- }
1536
- });
1537
-
1538
- const visitor$2 = function(commandObject, cli) {
1539
- commandObject.handler = wrapCommandhandler(commandObject.handler, function(args) {
1540
- Object.defineProperty(this, "engine", {
1541
- get() {
1542
- return cli;
1543
- }
1544
- });
1545
- Object.defineProperty(this, "log", {
1546
- get() {
1547
- return (msg, options2 = {}) => {
1548
- OutputUtils.log(msg, {
1549
- ...options2,
1550
- loggerInstance: cli.getInternalMethods().getLoggerInstance()
1551
- });
1552
- };
1553
- }
1554
- });
1555
- });
1556
- return commandObject;
1557
- };
1558
1607
  const options$2 = {};
1559
1608
 
1560
- const CommandHandlerInjections = new Plugin("built-in:command-handler-injections", {
1609
+ const DefaultCommandFallbacks = new Plugin("built-in:default-command-fallbacks", {
1561
1610
  created({ engine, builder, factoryOptions }) {
1562
- engine.globalCommandVisitor("command-handler-injections-visitor", visitor$2, options$2);
1611
+ engine.globalCommandVisitor("default-command-fallbacks-visitor", visitor$2, options$2);
1563
1612
  }
1564
1613
  });
1565
1614
 
@@ -1675,8 +1724,7 @@ CLIFactory.use(RegisterLintCommands);
1675
1724
  CLIFactory.use(RegisterReleaseCommand);
1676
1725
  CLIFactory.use(RegisterTestCommand);
1677
1726
  CLIFactory.use(DefaultCommandFallbacks);
1678
- CLIFactory.use(CommandHandlerInjections);
1679
1727
  CLIFactory.use(CommandMeta);
1680
1728
  CLIFactory.use(PrivateCommands);
1681
1729
 
1682
- export { CLIFactory, Command, InterfaceUtils, OutputUtils, extractUsage };
1730
+ export { CLIFactory, Command, InterfaceUtils, OutputUtils, Plugin, extractUsage };