juisy 2.0.0-beta.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 (71) hide show
  1. package/README.md +211 -0
  2. package/bin/cli/cli.js +23 -0
  3. package/bin/cli/cmds/changelog.js +41 -0
  4. package/bin/cli/cmds/docs/generate-api.js +22 -0
  5. package/bin/cli/cmds/docs/generate-cli.js +11 -0
  6. package/bin/cli/cmds/docs/generate-readme.js +11 -0
  7. package/bin/cli/cmds/docs/index.js +22 -0
  8. package/bin/cli/cmds/docs/lint.js +42 -0
  9. package/bin/cli/cmds/eject.js +28 -0
  10. package/bin/cli/cmds/git-hooks/index.js +20 -0
  11. package/bin/cli/cmds/git-hooks/reset.js +48 -0
  12. package/bin/cli/cmds/git-hooks/sync.js +19 -0
  13. package/bin/cli/cmds/index.js +15 -0
  14. package/bin/cli/cmds/print-globals.js +28 -0
  15. package/bin/cli/cmds/release.js +231 -0
  16. package/bin/cli/cmds/squeeze.js +269 -0
  17. package/bin/cli/cmds/test.js +33 -0
  18. package/bin/cli/index.js +9 -0
  19. package/bin/cli/lib/docs/generate-api-doc.js +78 -0
  20. package/bin/cli/lib/version/update-version.js +52 -0
  21. package/bin/scripts/commit-msg.js +32 -0
  22. package/bin/scripts/pre-commit.js +24 -0
  23. package/dist/DataExporter.d.ts +67 -0
  24. package/dist/cli/CLIFactory.d.ts +19 -0
  25. package/dist/cli/Command.d.ts +44 -0
  26. package/dist/cli/InterfaceUtils.d.ts +53 -0
  27. package/dist/cli/OutputUtils.d.ts +123 -0
  28. package/dist/cli/command-visitors/command-handler-injections.d.ts +10 -0
  29. package/dist/cli/command-visitors/get-command-meta.d.ts +10 -0
  30. package/dist/cli/command-visitors/index.d.ts +9 -0
  31. package/dist/cli/command-visitors/private-command.d.ts +16 -0
  32. package/dist/cli/create-engine.d.ts +7 -0
  33. package/dist/cli/extract-usage.d.ts +72 -0
  34. package/dist/cli/index.d.ts +20 -0
  35. package/dist/cli/index.js +559 -0
  36. package/dist/cli/types.d.ts +112 -0
  37. package/dist/cli/utils.d.ts +19 -0
  38. package/dist/eject.d.ts +22 -0
  39. package/dist/get-package-info.d.ts +6 -0
  40. package/dist/index.d.ts +11 -0
  41. package/dist/index.js +244 -0
  42. package/dist/project-globals.d.ts +63 -0
  43. package/dist/templater/Templater.d.ts +23 -0
  44. package/dist/templater/index.d.ts +6 -0
  45. package/dist/templater/index.js +330 -0
  46. package/dist/templater/markdown-templater/ReadmeTemplater.d.ts +154 -0
  47. package/dist/templater/markdown-templater/index.d.ts +25 -0
  48. package/dist/templater/types.d.ts +10 -0
  49. package/dist/utils/misc.d.ts +21 -0
  50. package/package.json +179 -0
  51. package/src/index.js +507 -0
  52. package/template/CHANGELOG.md +0 -0
  53. package/template/bin/cli/cli.js +27 -0
  54. package/template/bin/cli/cmds/changelog.js +71 -0
  55. package/template/bin/cli/cmds/docs.js +30 -0
  56. package/template/bin/cli/cmds/docs_cmds/generate-api.js +75 -0
  57. package/template/bin/cli/cmds/docs_cmds/generate-readme.js +51 -0
  58. package/template/bin/cli/cmds/git-hooks.js +30 -0
  59. package/template/bin/cli/cmds/git_hooks_cmds/reset.js +76 -0
  60. package/template/bin/cli/cmds/git_hooks_cmds/sync.js +44 -0
  61. package/template/bin/cli/cmds/release.js +219 -0
  62. package/template/bin/cli/index.js +7 -0
  63. package/template/bin/cli/lib/docs/generate-api-doc.js +33 -0
  64. package/template/bin/cli/lib/release/generate-release-note.js +3 -0
  65. package/template/bin/cli/lib/version/update-version.js +51 -0
  66. package/template/bin/scripts/commit-msg.js +42 -0
  67. package/template/bin/scripts/pre-commit.js +32 -0
  68. package/template/docs/api/docs.config.js +10 -0
  69. package/template/docs/readme/config.js +22 -0
  70. package/template/docs/readme/readme.js +70 -0
  71. package/template/docs/readme/template.md +53 -0
package/package.json ADDED
@@ -0,0 +1,179 @@
1
+ {
2
+ "name": "juisy",
3
+ "version": "2.0.0-beta.0",
4
+ "description": "Make your JavaScript (and/or TypeScript) project juicy!",
5
+ "main": "src/index.js",
6
+ "type": "module",
7
+ "files": [
8
+ "bin",
9
+ "dist",
10
+ "template"
11
+ ],
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ },
17
+ "./cli": {
18
+ "types": "./dist/cli/index.d.ts",
19
+ "import": "./dist/cli/index.js"
20
+ },
21
+ "./templater": {
22
+ "types": "./dist/templater/index.d.ts",
23
+ "import": "./dist/templater/index.js"
24
+ }
25
+ },
26
+ "imports": {
27
+ "#juisy": {
28
+ "types": "./dist/index.d.ts",
29
+ "import": "./dist/index.js"
30
+ },
31
+ "#juisy/cli": {
32
+ "types": "./dist/cli/index.d.ts",
33
+ "import": "./dist/cli/index.js"
34
+ },
35
+ "#juisy/templater": {
36
+ "types": "./dist/templater/index.d.ts",
37
+ "import": "./dist/templater/index.js"
38
+ }
39
+ },
40
+ "publishConfig": {
41
+ "access": "public"
42
+ },
43
+ "scripts": {
44
+ "build": "vite build",
45
+ "audit": "ncu && npm audit",
46
+ "audit:fix": "ncu --interactive",
47
+ "docs:api": "npx cross-env CLI_ENV=private node ./bin/cli docs generate:api",
48
+ "docs:readme": "npx cross-env CLI_ENV=private node ./bin/cli docs generate:readme",
49
+ "docs:cli": "npx cross-env CLI_ENV=private node ./bin/cli docs generate:cli",
50
+ "docs:lint": "npx cross-env CLI_ENV=private node ./bin/cli docs lint -c ./docs/.markdownlint-cli2.cjs",
51
+ "docs:lint:fix": "npm run docs:lint -- --fix",
52
+ "docs": "npm run docs:readme && npm run docs:cli && npm run docs:api && npm run docs:lint",
53
+ "lint": "eslint .",
54
+ "lint:fix": "npm run lint -- --fix",
55
+ "lint:markdown": "npm run docs:lint",
56
+ "lint:markdown:fix": "npm run docs:lint:fix",
57
+ "release": "npx cross-env CLI_ENV=private NODE_ENV=development node ./bin/cli release",
58
+ "changelog": "npx cross-env CLI_ENV=private NODE_ENV=development node ./bin/cli changelog",
59
+ "example:reset": "git checkout --no-overlay -- example && cd example && git clean -fdX",
60
+ "example:test": "npm run example:reset && cd example && npm install -D @hperchec/juisy@file:../ && npx juisy squeeze && npm run docs:readme",
61
+ "git-hooks:reset": "npx cross-env CLI_ENV=private NODE_ENV=development node ./bin/cli git-hooks reset",
62
+ "git-hooks:sync": "npx cross-env CLI_ENV=private NODE_ENV=development node ./bin/cli git-hooks sync",
63
+ "print:globals": "node --no-warnings ./bin/cli print:globals",
64
+ "test": "npx cross-env CLI_ENV=private node ./bin/cli test",
65
+ "test:dev": "npm test -- --watch",
66
+ "test:ui": "npm test -- --ui",
67
+ "__prepare": "npm run git-hooks:sync"
68
+ },
69
+ "repository": {
70
+ "type": "git",
71
+ "url": "git+https://gitlab.com/hperchec/juisy.git"
72
+ },
73
+ "keywords": [
74
+ "js",
75
+ "build",
76
+ "release",
77
+ "changelog",
78
+ "bin",
79
+ "cmd",
80
+ "easy"
81
+ ],
82
+ "author": {
83
+ "name": "Hervé Perchec",
84
+ "email": "contact@herve-perchec.com",
85
+ "url": "https://gitlab.com/herveperchec"
86
+ },
87
+ "license": "ISC",
88
+ "bugs": {
89
+ "url": "https://gitlab.com/hperchec/juisy/issues"
90
+ },
91
+ "homepage": "https://gitlab.com/hperchec/juisy#readme",
92
+ "bin": {
93
+ "juisy": "./bin/cli/index.js"
94
+ },
95
+ "simple-git-hooks": {
96
+ "pre-commit": "node ./bin/scripts/pre-commit.js",
97
+ "commit-msg": "node ./bin/scripts/commit-msg.js ${1}"
98
+ },
99
+ "lint-staged": {
100
+ "*.{js,cjs,ts}": [
101
+ "eslint --fix",
102
+ "git add"
103
+ ]
104
+ },
105
+ "peerDependencies": {
106
+ "@commitlint/cli": "^17.7.2",
107
+ "@commitlint/config-conventional": "^17.7.0",
108
+ "@github/markdownlint-github": "^0.6.0",
109
+ "@hperchec/readme-generator": "^3.0.0",
110
+ "@stylistic/eslint-plugin": "^2",
111
+ "@typescript-eslint/eslint-plugin": "^8",
112
+ "chalk": "^4.1.2",
113
+ "conventional-changelog-cli": "^4.1.0",
114
+ "eslint": "^9",
115
+ "execa": "^5.1.1",
116
+ "lint-staged": "^14.0.1",
117
+ "markdownlint-cli2": "^0.12.1",
118
+ "markdownlint-cli2-formatter-pretty": "^0.0.6",
119
+ "prompts": "^2.4.2",
120
+ "simple-git-hooks": "^2.9.0",
121
+ "yargs": "^17.7.2"
122
+ },
123
+ "devDependencies": {
124
+ "@commitlint/cli": "^19.6.1",
125
+ "@commitlint/config-conventional": "^19.6.0",
126
+ "@github/markdownlint-github": "^0.6.3",
127
+ "@rollup/plugin-typescript": "^12.1.2",
128
+ "@stylistic/eslint-plugin": "^2.12.1",
129
+ "@types/ejs": "^3.1.5",
130
+ "@types/fs-extra": "^11.0.4",
131
+ "@types/lodash.merge": "^4.6.9",
132
+ "@types/prompts": "^2.4.9",
133
+ "@types/yargs": "^17.0.33",
134
+ "@typescript-eslint/eslint-plugin": "^8.18.1",
135
+ "@vitest/coverage-v8": "^2.1.8",
136
+ "@vitest/ui": "^2.1.8",
137
+ "conventional-changelog-cli": "^5.0.0",
138
+ "eslint": "^9.17.0",
139
+ "happy-dom": "^15.11.7",
140
+ "lint-staged": "^14.0.1",
141
+ "markdown-table": "^3.0.4",
142
+ "markdownlint-cli2": "^0.16.0",
143
+ "markdownlint-cli2-formatter-pretty": "^0.0.7",
144
+ "npm-check-updates": "^17.1.12",
145
+ "typescript": "^5.7.2",
146
+ "vite": "^5.4.11",
147
+ "vite-plugin-dts": "^4.3.0",
148
+ "vitest": "^2.1.8"
149
+ },
150
+ "dependencies": {
151
+ "@dotenvx/dotenvx": "^1.31.0",
152
+ "ascii-tree": "^0.3.0",
153
+ "chalk": "^4.1.2",
154
+ "deepmerge": "^4.3.1",
155
+ "ejs": "^3.1.10",
156
+ "execa": "^5.1.1",
157
+ "find-up": "^7.0.0",
158
+ "fs-extra": "^11.2.0",
159
+ "github-slugger": "^2.0.0",
160
+ "glob": "^11.0.0",
161
+ "indent-string": "^5.0.0",
162
+ "json-2-csv": "^5.5.7",
163
+ "jstoxml": "^5.0.2",
164
+ "lodash.merge": "^4.6.2",
165
+ "loglevel": "^1.9.2",
166
+ "markdown-toc": "^1.2.0",
167
+ "markdown-utils": "^1.0.0",
168
+ "package-json-type": "^1.0.3",
169
+ "pkg-dir": "^8.0.0",
170
+ "prompts": "^2.4.2",
171
+ "remark": "^15.0.1",
172
+ "remark-frontmatter": "^5.0.0",
173
+ "remark-toc": "^9.0.0",
174
+ "simple-git-hooks": "^2.9.0",
175
+ "strip-ansi": "^7.1.0",
176
+ "yaml": "^2.6.1",
177
+ "yargs": "^17.7.2"
178
+ }
179
+ }
package/src/index.js ADDED
@@ -0,0 +1,507 @@
1
+ /**
2
+ * @file Entry point
3
+ * @description @hperchec/juisy [/src/index.js]
4
+ * @author Hervé Perchec
5
+ */
6
+
7
+ /**
8
+ * @module juisy
9
+ * @type {object}
10
+ * @typicalname juisy
11
+ * @description
12
+ * From: @hperchec/juisy@{{{{VERSION}}}}
13
+ * @example
14
+ * const juisy = require('@hperchec/juisy')
15
+ */
16
+
17
+ const getCallerFile = require('get-caller-file')
18
+ const Yargs = require('yargs/yargs')
19
+ const utils = require('./utils')
20
+
21
+ /**
22
+ * @param {Function} builder - The CLI builder function
23
+ * @returns {Function} A function that takes optional argv as first parameter and returns {@link CLI CLI}
24
+ * @description
25
+ * Creates a CLI (yargs instance) factory
26
+ * @example
27
+ * const { createCli } = require('@hperchec/juisy')
28
+ *
29
+ * const cli = createCli(cli => cli
30
+ * .scriptName('my-juicy-cli')
31
+ * .usage('Usage: $0 <command> [<options>]')
32
+ * )
33
+ *
34
+ * const argv = process.argv.slice(2)
35
+ *
36
+ * // Parse argv
37
+ * cli().parse(argv)
38
+ */
39
+ exports.createCli = function (builder) {
40
+ return function (argv) {
41
+ const cli = initYargs(Yargs(argv))
42
+ return builder(cli)
43
+ }
44
+ }
45
+
46
+ /**
47
+ * @typedef {object} CommandDoclet
48
+ * @category types
49
+ * @description
50
+ * Command documentation object returned by {@link module:juisy.cliTools.extractUsage extractUsage} tool.
51
+ * @property {string} command - The command
52
+ * @property {string[]} args - The command args
53
+ * @property {object} aliases - The yargs instance `aliasMap` reference
54
+ * @property {object} deprecated - If command is deprecated
55
+ * @property {module:juisy~ExtractedUsage} extractedUsage - The extracted usage object from yargs usage instance
56
+ * @property {string} rawUsage - The usage output as string (as if `--help` option passed)
57
+ * @property {?object} [children] - The child commands
58
+ */
59
+
60
+ /**
61
+ * @typedef {object} ExtractedUsage
62
+ * @category types
63
+ * @description
64
+ * Type of {@link module:juisy~CommandDoclet CommandDoclet} `extractedUsage` property
65
+ * @property {object} demandedCommands - Same as yargs instance `getDemandedCommands` method
66
+ * @property {object} demandedOptions - Same as yargs instance `getDemandedOptions` method
67
+ * @property {object} deprecatedOptions - Same as yargs instance `getDeprecatedOptions` method
68
+ * @property {object} groups - Same as yargs instance `getGroups` method
69
+ * @property {object} options - Same as yargs instance `getOptions` method
70
+ * @property {string[]} usages - Same as yargs usage instance `getUsage` method
71
+ * @property {object} commands - The result of yargs usage instance `getCommands` method as object.
72
+ * While the UsageInstance commands items are defined as follow: `[cmd, description, isDefault, aliases, deprecated]`,
73
+ * we cast it to an object like: `{cmd, description, isDefault, aliases, deprecated}`.
74
+ */
75
+
76
+ /**
77
+ * @alias module:juisy.cliTools
78
+ * @type {object}
79
+ * @description
80
+ * CLI tools
81
+ */
82
+ const cliTools = exports.cliTools = {
83
+ /**
84
+ * @async
85
+ * @param {Function} cliFactory - The cli factory function returned by createCli method
86
+ * @param {boolean} [recursive = false] - Recursive mode (parse child commands)
87
+ * @param {string[]} [args = [""]] - The base args to pass as parseAsync first parameter
88
+ * @param {string} [locale = en] - The locale
89
+ * @returns {Promise<module:juisy~CommandDoclet>} The extracted command doclet
90
+ * @fulfil {module:juisy~CommandDoclet}
91
+ * @description
92
+ * Extract usage object from CLI (Yargs) instance factory
93
+ * @example
94
+ * **Note**: don't call `cli()`, pass the factory.
95
+ *
96
+ * ```js
97
+ * const { createCli, cliTools } = require('@hperchec/juisy')
98
+ *
99
+ * const cli = createCli(cli => cli
100
+ * // ...
101
+ * )
102
+ *
103
+ * cliTools.extractUsage(cli)
104
+ * ```
105
+ *
106
+ * Recursively extract usage:
107
+ *
108
+ * ```js
109
+ * await cliTools.extractUsage(cli, true)
110
+ * ```
111
+ * With base args:
112
+ *
113
+ * ```js
114
+ * await cliTools.extractUsage(cli, false, [ 'my-command' ])
115
+ * ```
116
+ *
117
+ * Change locale:
118
+ *
119
+ * ```js
120
+ * await cliTools.extractUsage(cli, undefined, undefined, 'fr')
121
+ * ```
122
+ */
123
+ async extractUsage (cliFactory, recursive = false, args = [ '' ], locale = 'en') {
124
+ const innerYargs = cliFactory([ '' ]) // empty argv
125
+ // Set locale
126
+ innerYargs.locale(locale)
127
+
128
+ // Init doclet object extracted usage object
129
+ const doclet = {
130
+ command: undefined,
131
+ args: undefined,
132
+ aliases: undefined,
133
+ // deprecated: false,
134
+ extractedUsage: undefined,
135
+ rawUsage: undefined,
136
+ children: recursive ? {} : undefined
137
+ }
138
+
139
+ const parseCallback = function (err, argv, output) {
140
+ if (err) throw err
141
+ // this is the yargs instance with temporary parse state
142
+ const self = this
143
+ // Set doclet args
144
+ doclet.args = argv._
145
+ // Set doclet command
146
+ doclet.command = argv._[argv._.length - 1] // is there a better way?
147
+ // Set rawUsage
148
+ doclet.rawUsage = output
149
+ // Extracted usage
150
+ const extractedUsage = doclet.extractedUsage = {}
151
+
152
+ extractedUsage.demandedCommands = self.getDemandedCommands()
153
+ extractedUsage.demandedOptions = self.getDemandedOptions()
154
+ extractedUsage.deprecatedOptions = self.getDeprecatedOptions()
155
+ extractedUsage.groups = self.getGroups()
156
+ extractedUsage.options = self.getOptions()
157
+ // extractedUsage.examples = examples // @todo: No API for UsageInstance examples?
158
+
159
+ const internalMethods = self.getInternalMethods()
160
+ const usageInstance = internalMethods.getUsageInstance()
161
+
162
+ extractedUsage.usages = usageInstance.getUsage()
163
+
164
+ const commandInstance = internalMethods.getCommandInstance()
165
+
166
+ // Set doclet aliases
167
+ doclet.aliases = commandInstance.aliasMap
168
+
169
+ const childCommandsFromHandlers = commandInstance.handlers
170
+ const childCommandsFromUsage = usageInstance.getCommands()
171
+
172
+ extractedUsage.commands = Object.keys(childCommandsFromHandlers).reduce((accumulator, handlerKey) => {
173
+ // the UsageInstance commands item is like: [cmd, description, isDefault, aliases, deprecated]
174
+ const [ cmd, description, isDefault, aliases, deprecated ] = childCommandsFromUsage.find((command) => {
175
+ return command[0] === childCommandsFromHandlers[handlerKey].original
176
+ })
177
+ accumulator[handlerKey] = { cmd, description, isDefault, aliases, deprecated }
178
+ return accumulator
179
+ }, {})
180
+ }
181
+
182
+ // Parse async command
183
+ const parsed = await innerYargs.parseAsync(args, { help: true }, parseCallback) // eslint-disable-line no-unused-vars
184
+
185
+ // If recursive, loop on child commands
186
+ if (recursive) {
187
+ for (const childCommand in doclet.extractedUsage.commands) {
188
+ // Recursively call extractUsage method
189
+ doclet.children[childCommand] = await cliTools.extractUsage(
190
+ cliFactory,
191
+ true,
192
+ [ ...(args.filter(a => a !== '')), childCommand ],
193
+ locale
194
+ )
195
+ }
196
+ }
197
+
198
+ return doclet
199
+ }
200
+ }
201
+
202
+ /**
203
+ * @type {object}
204
+ * @description
205
+ * Juisy utils. See [utils](./utils.md) documentation.
206
+ */
207
+ exports.utils = utils
208
+
209
+ /**
210
+ * @ignore
211
+ * @param {Yargs} yargs - The yargs instance
212
+ * @returns {CLI} The initialized yargs as CLI
213
+ */
214
+ function initYargs (yargs) {
215
+ // Private properties
216
+ yargs._isPrivate = false
217
+ yargs._globalCommandVisitors = {}
218
+ yargs._globalCommandVisitorsEnabled = new Set([])
219
+ yargs._meta = {}
220
+
221
+ // Methods
222
+ const originalCommandDirMethod = yargs.commandDir
223
+ yargs.originalCommandDir = originalCommandDirMethod
224
+ yargs.commandDir = commandDir
225
+ yargs.globalCommandVisitor = globalCommandVisitor
226
+ yargs.globalCommandVisitorOptions = globalCommandVisitorOptions
227
+ yargs.hasGlobalCommandVisitor = hasGlobalCommandVisitor
228
+ yargs.disableGlobalCommandVisitors = disableGlobalCommandVisitors
229
+ yargs.enableGlobalCommandVisitors = enableGlobalCommandVisitors
230
+ yargs.isPrivate = isPrivate
231
+ yargs.getMeta = getMeta
232
+
233
+ return yargs
234
+ .globalCommandVisitor('private-command-visitor', privateCommandVisitor, privateCommandVisitor.options)
235
+ .globalCommandVisitor('get-meta-command-visitor', getMetaCommandVisitor, getMetaCommandVisitor.options)
236
+ .strict() // disallow unknow command
237
+ .help() // Enable help
238
+ }
239
+
240
+ /**
241
+ * @typedef {Yargs} CLI
242
+ * @typicalname cli
243
+ * @category types
244
+ * @description
245
+ * CLI object returned by cli factory from {@link module:juisy.createCli createCli} method.
246
+ *
247
+ * It has all inherited properties and methods from [yargs](https://github.com/yargs/yargs) instance plus the following:
248
+ */
249
+
250
+ /**
251
+ * @alias module:juisy~CLI#commandDir
252
+ * @param {string} dir - Same as yargs `commandDir` argument
253
+ * @param {object} [options] - Same as yargs `commandDir` options
254
+ * @returns {CLI} Returns the CLI (yargs) instance
255
+ * @description
256
+ * See [yargs.commandDir](https://yargs.js.org/docs/#api-reference-commanddirdirectory-opts) documentation.
257
+ */
258
+ function commandDir (dir, options = {}) {
259
+ const self = this // this is yargs instance
260
+ // Is options.visit set?
261
+ const visitor = options.visit || (cmdObj => cmdObj)
262
+
263
+ // Add directory with wrapped visit option
264
+ self.getInternalMethods().getCommandInstance().addDirectory(dir, require, getCallerFile(), {
265
+ ...options,
266
+ visit: function (commandObject, pathToFile, filename) {
267
+ let failOnFalsyReturn
268
+ // Loop on enabled global command visitors
269
+ for (const name of self._globalCommandVisitorsEnabled) {
270
+ const globalVisitor = self._globalCommandVisitors[name]
271
+ // Call global visitor: fail on falsy returned value
272
+ if (!globalVisitor(commandObject, pathToFile, filename, self, globalVisitor.options)) {
273
+ failOnFalsyReturn = true
274
+ }
275
+ }
276
+
277
+ // Then custom visitor
278
+ const customVisitorResult = visitor(commandObject, pathToFile, filename, self)
279
+
280
+ return failOnFalsyReturn ? false : customVisitorResult
281
+ }
282
+ })
283
+ return self
284
+ }
285
+
286
+ /**
287
+ * @callback module:juisy~CLI~globalCommandVisitorCallback
288
+ * @category callback
289
+ * @param {object} commandObject - The command object
290
+ * @param {string} pathToFile - The path to file
291
+ * @param {string} filename - The filename
292
+ * @param {CLI} yargs - The current CLI instance
293
+ * @param {object} options - The default options to set
294
+ * @description
295
+ * Custom global command visitor callback.
296
+ *
297
+ * Takes same parameters as original yargs commandDir "visit" option, plus yargs itself and visitor options.
298
+ */
299
+
300
+ /**
301
+ * @alias module:juisy~CLI#globalCommandVisitor
302
+ * @param {string} name - The visitor unique name
303
+ * @param {module:juisy~CLI~globalCommandVisitorCallback} visitor - The visitor function
304
+ * @param {object} defaultOptions - The default visitor options object
305
+ * @returns {CLI} Returns the CLI (yargs) instance
306
+ * @description
307
+ * Set a global command visitor function
308
+ * @example
309
+ * const visitor = function (commandObject, pathToFile, filename, yargs, options) {
310
+ * const { foo } = options
311
+ * console.log('The "foo" option : ', foo)
312
+ * return commandObject
313
+ * }
314
+ *
315
+ * cli.globalCommandVisitor('my-custom-visitor', visitor, { foo: 'bar' })
316
+ * .commandDir('cmds_dir')
317
+ * // => will console log foo option for each command visited
318
+ */
319
+ function globalCommandVisitor (name, visitor, defaultOptions = {}) {
320
+ this._globalCommandVisitors[name] = visitor
321
+ return this.enableGlobalCommandVisitors([ name ])
322
+ .globalCommandVisitorOptions(name, defaultOptions)
323
+ }
324
+
325
+ /**
326
+ * @alias module:juisy~CLI#globalCommandVisitorOptions
327
+ * @param {string} name - The visitor unique name
328
+ * @param {object} options - The visitor options object
329
+ * @returns {CLI} Returns the CLI (yargs) instance
330
+ * @description
331
+ * Set global command visitor options
332
+ * @example
333
+ * // Overrides default options for 'my-custom-visitor'
334
+ * // defined via globalCommandVisitor method
335
+ * cli.globalCommandVisitorOptions('my-custom-visitor', { foo: 'baz' })
336
+ */
337
+ function globalCommandVisitorOptions (name, options) {
338
+ this._globalCommandVisitors[name].options = {
339
+ ...this._globalCommandVisitors[name].options,
340
+ ...options
341
+ }
342
+ return this
343
+ }
344
+
345
+ /**
346
+ * @alias module:juisy~CLI#hasGlobalCommandVisitor
347
+ * @param {string} name - The visitor name
348
+ * @returns {boolean} True if global command visitor is defined
349
+ * @description
350
+ * Check if global command visitor is defined
351
+ * @example
352
+ * cli.hasGlobalCommandVisitor('my-custom-visitor')
353
+ */
354
+ function hasGlobalCommandVisitor (name) {
355
+ return Object.keys(this._globalCommandVisitors).includes(name)
356
+ }
357
+
358
+ /**
359
+ * @alias module:juisy~CLI#disableGlobalCommandVisitors
360
+ * @param {string[]} visitors - The visitor names
361
+ * @returns {CLI} Returns the CLI (yargs) instance
362
+ * @description
363
+ * Disables global command visitors
364
+ * @example
365
+ * // Disables the "my-disabled-visitor" global command visitor
366
+ * cli.disableGlobalCommandVisitors(['my-disabled-visitor'])
367
+ */
368
+ function disableGlobalCommandVisitors (visitors = []) {
369
+ for (const name of visitors) {
370
+ // If visitor exists
371
+ if (this.hasGlobalCommandVisitor(name)) {
372
+ // Enable
373
+ this._globalCommandVisitorsEnabled.delete(name)
374
+ } else {
375
+ throw new Error('Global command visitor: "' + name + '" not defined.')
376
+ }
377
+ }
378
+ return this
379
+ }
380
+
381
+ /**
382
+ * @alias module:juisy~CLI#enableGlobalCommandVisitors
383
+ * @param {string[]} visitors - The visitor names
384
+ * @returns {CLI} Returns the CLI (yargs) instance
385
+ * @description
386
+ * Enables global command visitors
387
+ * @example
388
+ * // Enables the "my-custom-visitor" and "my-awesome-visitor"
389
+ * // global command visitors
390
+ * cli.enableGlobalCommandVisitors(['my-custom-visitor', 'my-awesome-visitor'])
391
+ */
392
+ function enableGlobalCommandVisitors (visitors = []) {
393
+ for (const name of visitors) {
394
+ // If visitor exists
395
+ if (this.hasGlobalCommandVisitor(name)) {
396
+ // Enable
397
+ this._globalCommandVisitorsEnabled.add(name)
398
+ } else {
399
+ throw new Error('Global command visitor: "' + name + '" not defined.')
400
+ }
401
+ }
402
+ return this
403
+ }
404
+
405
+ /**
406
+ * @alias module:juisy~CLI#isPrivate
407
+ * @returns {boolean} True if command is private
408
+ * @description
409
+ * Get command private status
410
+ */
411
+ function isPrivate () {
412
+ return this._isPrivate
413
+ }
414
+
415
+ /**
416
+ * @alias module:juisy~CLI#getMeta
417
+ * @returns {object} The command meta
418
+ * @description
419
+ * Get the CLI instance meta
420
+ * @example
421
+ * cli.getMeta()
422
+ */
423
+ function getMeta () {
424
+ return this._meta
425
+ }
426
+
427
+ /**
428
+ * @ignore
429
+ * @param {?Function} target - The target builder
430
+ * @param {Function} builder - The wrap
431
+ * @description
432
+ * Wrap command builder (target) with builder passed as second parameter
433
+ * that will be called before target. Target can be undefined
434
+ */
435
+ function wrapBuilder (target, builder) {
436
+ // Is target set?
437
+ const _target = target || (yargs => yargs)
438
+ // Return wrapped builder
439
+ return function (yargs) {
440
+ return _target(builder(yargs))
441
+ }
442
+ }
443
+
444
+ /**
445
+ * @ignore
446
+ * @param {object} commandObject - Same as yargs.commandDir "visit" option
447
+ * @param {string} pathToFile - Same as yargs.commandDir "visit" option
448
+ * @param {string} filename - Same as yargs.commandDir "visit" option
449
+ * @param {CLI} cli - The CLI (yargs) instance
450
+ * @param {object} options - The global command visitor defined options
451
+ * @returns {object|boolean} False if command is private and env key is set to "private".
452
+ * Otherwise, returns the commandObject with wrapped builder to tag CLI instance as private.
453
+ * @description
454
+ * Global command visitor to auto-set command private status
455
+ */
456
+ function privateCommandVisitor (commandObject, pathToFile, filename, cli, options) {
457
+ const { cmdObjectProp, envKey } = options
458
+ if (commandObject[cmdObjectProp]) {
459
+ // Check if env key is set to 'private'
460
+ if (process.env[envKey] === 'private') {
461
+ // Wrap builder
462
+ commandObject.builder = wrapBuilder(commandObject.builder, function (yargs) {
463
+ yargs._isPrivate = true
464
+ return yargs
465
+ })
466
+ } else {
467
+ // Else, don't return command object
468
+ return false
469
+ }
470
+ }
471
+ return commandObject
472
+ }
473
+
474
+ privateCommandVisitor.options = {
475
+ cmdObjectProp: 'private',
476
+ envKey: 'CLI_ENV'
477
+ }
478
+
479
+ /**
480
+ * @ignore
481
+ * @param {object} commandObject - Same as yargs.commandDir "visit" option
482
+ * @param {string} pathToFile - Same as yargs.commandDir "visit" option
483
+ * @param {string} filename - Same as yargs.commandDir "visit" option
484
+ * @param {CLI} cli - The CLI (yargs) instance
485
+ * @param {object} options - The global command visitor defined options
486
+ * @returns {object} The commandObject with wrapped builder to inject CLI instance meta.
487
+ * @description
488
+ * Global command visitor to auto-set command meta
489
+ */
490
+ function getMetaCommandVisitor (commandObject, pathToFile, filename, cli, options) {
491
+ const { cmdObjectProp } = options
492
+ // if prop is defined (default: "meta")
493
+ if (commandObject[cmdObjectProp]) {
494
+ const commandMeta = commandObject[cmdObjectProp]
495
+ // Wrap builder
496
+ commandObject.builder = wrapBuilder(commandObject.builder, function (yargs) {
497
+ // Deep merge the meta objects with parent
498
+ yargs._meta = utils.merge(yargs._meta, commandMeta)
499
+ return yargs
500
+ })
501
+ }
502
+ return commandObject
503
+ }
504
+
505
+ getMetaCommandVisitor.options = {
506
+ cmdObjectProp: 'meta'
507
+ }
File without changes