@gesslar/bedoc 1.5.2 → 1.6.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.
package/package.json CHANGED
@@ -1,8 +1,10 @@
1
1
  {
2
2
  "name": "@gesslar/bedoc",
3
- "version": "1.5.2",
3
+ "version": "1.6.1",
4
4
  "description": "Pluggable documentation engine for any language and format",
5
5
  "publisher": "gesslar",
6
+ "author": "gesslar",
7
+ "license": "Unlicense",
6
8
  "main": "./src/core/Core.js",
7
9
  "repository": {
8
10
  "type": "git",
@@ -39,6 +41,7 @@
39
41
  "dotenv": "^16.4.7",
40
42
  "error-stack-parser": "^2.1.4",
41
43
  "globby": "^14.0.2",
44
+ "json5": "^2.2.3",
42
45
  "micromatch": "^4.0.8",
43
46
  "node-fetch": "^3.3.2",
44
47
  "yaml": "^2.7.0"
@@ -49,8 +52,8 @@
49
52
  "@typescript-eslint/parser": "^8.22.0",
50
53
  "axios": "^1.7.9",
51
54
  "chokidar": "^4.0.3",
52
- "eslint-plugin-jsdoc": "^50.6.3",
53
55
  "eslint": "^9.18.0",
56
+ "eslint-plugin-jsdoc": "^50.6.3",
54
57
  "form-data": "^4.0.1"
55
58
  },
56
59
  "keywords": [
@@ -62,19 +65,5 @@
62
65
  "language",
63
66
  "format",
64
67
  "hooks"
65
- ],
66
- "author": "gesslar",
67
- "license": "Unlicense",
68
- "contributes": {
69
- "commands": [
70
- {
71
- "command": "vscode-bedoc.generateDocs",
72
- "title": "BeDoc: Generate Documentation"
73
- }
74
- ]
75
- },
76
- "extensionKind": [
77
- "workspace",
78
- "ui"
79
68
  ]
80
69
  }
package/src/cli.js CHANGED
@@ -72,6 +72,7 @@ const {resolveDirectory} = FDUtil
72
72
  },
73
73
  source: Environment.CLI
74
74
  })
75
+
75
76
  const filesToProcess = bedoc.options.input.map(f => f.absolutePath)
76
77
  const result = await bedoc.processFiles(filesToProcess)
77
78
  const errored = result.errored
@@ -71,9 +71,7 @@ export default class ActionManager {
71
71
  if(!setup)
72
72
  return
73
73
 
74
- await this.action.setup.call(
75
- this.action, {parent: this, log: this.#log}
76
- )
74
+ await this.action.setup.call(this.action, {log: this.#log})
77
75
  }
78
76
 
79
77
  async #cleanupAction() {
@@ -1,5 +1,6 @@
1
1
  import process from "node:process"
2
2
  import {Environment} from "./Core.js"
3
+ import JSON5 from "json5"
3
4
 
4
5
  import {
5
6
  ConfigurationParameters,
@@ -41,7 +42,9 @@ export default class Configuration {
41
42
  )
42
43
 
43
44
  const allOptions = this.#findAllOptions(options)
45
+
44
46
  Object.assign(finalOptions, await this.#mergeOptions(allOptions))
47
+
45
48
  this.#fixOptionValues(finalOptions)
46
49
 
47
50
  // Priority keys are those which must be processed first. They are
@@ -78,6 +81,12 @@ export default class Configuration {
78
81
  )
79
82
  }
80
83
 
84
+ // Check for mandatory values
85
+ for(const [key, {required}] of Object.entries(ConfigurationParameters)) {
86
+ if(required && !orderedSections.find(s => s.key === key))
87
+ throw new SyntaxError(`Missing mandatory key \`${key}\``)
88
+ }
89
+
81
90
  for(const section of orderedSections) {
82
91
  const {key} = section
83
92
 
@@ -212,12 +221,11 @@ export default class Configuration {
212
221
  */
213
222
  #findAllOptions(entryOptions) {
214
223
  const allOptions = []
215
-
216
224
  const environmentVariables = this.#getEnvironmentVariables()
217
225
  if(environmentVariables)
218
226
  allOptions.push({source: "environment", options: environmentVariables})
219
227
 
220
- const packageJson = entryOptions.packageJson
228
+ const packageJson = entryOptions?.packageJson
221
229
  if(packageJson?.bedoc)
222
230
  allOptions.push({source: "packageJson", options: packageJson.bedoc})
223
231
 
@@ -228,12 +236,16 @@ export default class Configuration {
228
236
  environmentVariables?.config
229
237
 
230
238
  if(useConfig) {
231
- const configFilename = packageJson?.bedoc?.config || entryOptions.config
232
-
233
- if(!configFilename)
239
+ const configFile =
240
+ packageJson?.bedoc?.config
241
+ ? resolveFilename(packageJson?.bedoc?.config)
242
+ : entryOptions.config?.value
243
+ ? resolveFilename(entryOptions.config.value)
244
+ : null
245
+
246
+ if(!configFile)
234
247
  throw new Error("No config file specified")
235
248
 
236
- const configFile = resolveFilename(configFilename)
237
249
  const config = loadJson(configFile)
238
250
 
239
251
  allOptions.push({source: "config", options: config})
@@ -303,7 +315,7 @@ export default class Configuration {
303
315
 
304
316
  // Last, but not least, add any defaulted options that are not in the
305
317
  // mapped options
306
- for(const [key, value] of Object.entries(entryOptions)) {
318
+ for(const [key, value] of Object.entries(entryOptions ?? {})) {
307
319
  if(!mappedOptions[key]) {
308
320
  if(value.source)
309
321
  mappedOptions[key] = value.value
@@ -329,7 +341,7 @@ export default class Configuration {
329
341
  switch(param.type.toString()) {
330
342
  case "boolean":
331
343
  case "number":
332
- options[key] = JSON.parse(options[key])
344
+ options[key] = JSON5.parse(options[key])
333
345
  break
334
346
  }
335
347
  }
package/src/core/Core.js CHANGED
@@ -45,11 +45,9 @@ export default class Core {
45
45
  debug("Creating new BeDoc instance with options: `%o`", 2, validConfig)
46
46
 
47
47
  const discovery = new Discovery(instance)
48
- const {printer: validPrint, parser: validParse} = validConfig
49
-
50
48
  const actionDefs = await discovery.discoverActions({
51
- print: validPrint,
52
- parse: validParse
49
+ print: validConfig.printer,
50
+ parse: validConfig.parser
53
51
  })
54
52
 
55
53
  const validCrit = discovery.satisfyCriteria(actionDefs, validConfig)
@@ -97,7 +95,7 @@ export default class Core {
97
95
  for(const [, value] of Object.entries(finalActions)) {
98
96
  const {action: actionType} = value.action.meta
99
97
 
100
- debug("Attaching `%o` action to instance", 2, actionType)
98
+ debug("Attaching %o action to instance", 2, actionType)
101
99
  instance.actions[actionType] = new managers[actionType](
102
100
  value, instance.logger
103
101
  )
@@ -34,6 +34,8 @@ export default class Discovery {
34
34
 
35
35
  debug("Discovering actions", 2)
36
36
 
37
+ debug("Specific modules provided: %o", 2, specific)
38
+
37
39
  const bucket = []
38
40
  const options = this.core.options ?? {}
39
41
 
@@ -50,10 +52,10 @@ export default class Discovery {
50
52
  debug("Mock path not set, discovering actions in node_modules", 1)
51
53
 
52
54
  debug("Looking for actions in project's package.json", 2)
53
- if(this.core.packageJson?.bedoc?.modules) {
54
- const actions = this.core.packageJson?.bedoc?.modules
55
+ if(this.core.packageJson?.modules) {
56
+ const actions = this.core.packageJson?.modules
55
57
 
56
- debug("Found %d actions in package.json: %d", 3, actions)
58
+ debug("Found %o actions in package.json", 3, actions)
57
59
  debug("Actions found in package.json action in package.json: %o", 3, actions)
58
60
 
59
61
  if(actions && typeof(actions) === "object")
@@ -70,7 +72,7 @@ export default class Discovery {
70
72
  execSync("npm root -g").toString().trim(),
71
73
  ]
72
74
 
73
- debug("Found %d directories to search for actions", 2, directories.length)
75
+ debug("Found %o directories to search for actions", 2, directories.length)
74
76
  debug("Directories to search for actions: %o", 3, directories)
75
77
 
76
78
  const moduleDirectories = directories
@@ -79,15 +81,15 @@ export default class Discovery {
79
81
  for(const moduleDirectory of moduleDirectories) {
80
82
  const {directories: dirs} = await ls(moduleDirectory.absolutePath)
81
83
 
82
- debug("Found %d directories in `%s`", 2,
84
+ debug("Found %o directories in `%s`", 2,
83
85
  dirs.length, moduleDirectory.absolutePath
84
86
  )
85
87
 
86
88
  const bedocDirs = dirs.filter(d => d.name.startsWith("bedoc-"))
87
- debug("Found %d bedoc directories under %s", 2, bedocDirs.length, moduleDirectory.absolutePath)
89
+ debug("Found %o bedoc directories under %s", 2, bedocDirs.length, moduleDirectory.absolutePath)
88
90
 
89
91
  const exports = bedocDirs.map(d => this.#getModuleExports(d))
90
- debug("Found %d module exports under %s", 2, exports.length, moduleDirectory.absolutePath)
92
+ debug("Found %o module exports under %s", 2, exports.length, moduleDirectory.absolutePath)
91
93
 
92
94
  bucket.push(...exports.flat())
93
95
  }
@@ -127,38 +129,39 @@ export default class Discovery {
127
129
  * respective contracts.
128
130
  *
129
131
  * @param {object[]} moduleFiles The module file objects to process
130
- * @param {object} specific The specific actions to load
132
+ * @param {object} specificModules The specific modules to load
131
133
  * @returns {Promise<object>} The discovered action
132
134
  */
133
- async #loadActionsAndContracts(moduleFiles, specific) {
135
+ async #loadActionsAndContracts(moduleFiles, specificModules) {
134
136
  const debug = this.#debug
135
137
 
136
138
  debug("Loading actions and contracts", 2)
137
139
  debug("Loading %d module files", 2, moduleFiles.length)
138
- debug("Specific actions to load: %o", 2, specific)
140
+ debug("Specific modules to load: %o", 2, specificModules)
139
141
 
140
142
  const resultActions = {}
141
143
  actionTypes.forEach(actionType => (resultActions[actionType] = []))
142
144
 
143
145
  // Tag the specific actions to load, so we can filter them later
144
- for(const [type, file] of Object.entries(specific)) {
146
+ for(const [type, file] of Object.entries(specificModules)) {
145
147
  if(file) {
146
- debug("Tagging specific action `%s` as `%s`", 3, file.absolutePath, type)
147
- file.specificType = type
148
+ debug("Tagging specific module `%s` as `%s`", 3, file.absolutePath, type)
149
+ file.specificType = file.specificType || []
150
+ file.specificType.push(type)
148
151
  }
149
152
  }
150
153
 
151
154
  const toLoad = [
152
155
  ...moduleFiles,
153
- ...Object.values(specific).filter(Boolean),
156
+ ...Object.values(specificModules).filter(Boolean),
154
157
  ]
155
158
 
156
- debug("Loading %d combined actions", 2, toLoad.length)
157
- debug("Actions to load: %o", 3, toLoad)
159
+ debug("Loading %d discovered modules", 2, toLoad.length)
160
+ debug("Modules to load: %o", 3, toLoad)
158
161
 
159
162
  const loadedActions = []
160
163
  for(const file of toLoad) {
161
- debug("Loading module `%s`", 2, file.absolutePath)
164
+ debug("Loading module `%s`", 2, file.absoluteUri)
162
165
 
163
166
  const loading = await this.#loadModule(file)
164
167
  const loaded = loading.actions.map((action, index) => {
@@ -170,19 +173,21 @@ export default class Discovery {
170
173
  }
171
174
 
172
175
  debug("Loaded %d actions", 2, loadedActions.length)
176
+ debug("Loaded actions", 3, loadedActions)
173
177
 
174
- const filtered = []
178
+ const filteredActions = []
175
179
  for(const actionType of actionTypes) {
176
- const file = specific[actionType]
180
+ const module = specificModules[actionType]
177
181
  const matchingActions = []
178
- if(file) {
179
- debug("Filtering actions for specific `%s`", 2, actionType)
182
+ if(module) {
183
+ debug("Filtering actions for specific: %o", 2, actionType)
180
184
  const found = loadedActions.find(
181
- e => e.file.absolutePath === file.absolutePath
185
+ e => e.file.specificType?.includes(actionType) &&
186
+ e.action.meta?.action === actionType
182
187
  )
183
188
 
184
189
  if(!found)
185
- throw new Error(`Could not find specific action: ${file.absolutePath}`)
190
+ throw new Error(`Could not find specific action: ${module.absolutePath}`)
186
191
 
187
192
  matchingActions.push(found)
188
193
  } else {
@@ -198,15 +203,17 @@ export default class Discovery {
198
203
  matchingActions.length, actionType
199
204
  )
200
205
 
201
- filtered.push(...matchingActions)
206
+ filteredActions.push(...matchingActions)
202
207
  }
203
208
 
204
- debug("Filtered %d actions", 2, filtered.length)
209
+ debug("Filtered %d actions", 2, filteredActions.length)
210
+ debug("Filtered actions %o", 3, filteredActions)
205
211
 
206
212
  // Now check the metas for validity
207
- for(const e of filtered) {
208
- const {action, contract, file: moduleFile} = e
213
+ for(const filtered of filteredActions) {
214
+ const {action, contract, file: moduleFile} = filtered
209
215
  const meta = action.meta
216
+
210
217
  if(!meta)
211
218
  throw new TypeError("Action has no meta object:\n" +
212
219
  JSON.stringify(moduleFile, null, 2) + "\n" +
@@ -222,7 +229,7 @@ export default class Discovery {
222
229
 
223
230
  const isValid = this.#validMeta(metaAction, {action, contract})
224
231
 
225
- debug("Action `%o` in `%s` is %s", 3,
232
+ debug("Meta in action %o in %o is %o", 3,
226
233
  metaAction, moduleFile.module, isValid ? "valid" : "invalid"
227
234
  )
228
235
 
@@ -239,7 +246,7 @@ export default class Discovery {
239
246
 
240
247
  for(const actionType of actionTypes) {
241
248
  const total = resultActions[actionType].length
242
- debug("Found %d `%s` actions", 2, total, actionType)
249
+ debug("Found %o `%o` actions", 2, total, actionType)
243
250
  }
244
251
 
245
252
  const total = Object.keys(resultActions).reduce((acc, curr) => {
@@ -255,6 +262,9 @@ export default class Discovery {
255
262
 
256
263
  satisfyCriteria(actions, validatedConfig) {
257
264
  const debug = this.#debug
265
+
266
+ debug("Available actions to check %o", 3, actions)
267
+
258
268
  const satisfied = {parse: [], print: []}
259
269
  const toMatch = {
260
270
  parse: {criterion: "language", config: "parser"},
@@ -272,10 +282,10 @@ export default class Discovery {
272
282
  if(validatedConfig[config]) {
273
283
  debug("Checking for specific `%s` action", 3, actionType)
274
284
  const found = actions[actionType].find(
275
- a => a.file.specificType === actionType
285
+ a => a.file.specificType.includes(actionType)
276
286
  )
277
287
  if(found) {
278
- debug("Found specific `%s` action", 3, actionType)
288
+ debug("Found specific %o action", 3, actionType)
279
289
  satisfied[actionType].push(found)
280
290
  continue
281
291
  }
@@ -285,13 +295,14 @@ export default class Discovery {
285
295
 
286
296
  // Hmm! We didn't find anything specific. Let's check the criterion
287
297
  debug("Checking for `%s` actions with criterion `%s`", 3, actionType, criterion)
298
+ debug("Validated config to check against: %o", 3, validatedConfig)
288
299
  const found = actions[actionType].filter(a => {
289
300
  debug("Meta criterion value: %o", 4, a.action.meta[criterion])
290
301
  debug("Config criterion value: %o", 4, validatedConfig[criterion])
291
302
  return a.action.meta[criterion] === validatedConfig[criterion]
292
303
  })
293
304
 
294
- debug("Found %d `%s` actions with criterion `%s`", 3,
305
+ debug("Found %o %o actions with criterion %o", 3,
295
306
  found.length, actionType, criterion
296
307
  )
297
308
 
@@ -90,6 +90,7 @@ export default class HookManager {
90
90
  return null
91
91
 
92
92
  hooksObj.log = instance.log
93
+ hooksObj.timeout = this.timeout
93
94
  instance.#hooks = hooksObj
94
95
 
95
96
  debug("Hooks loaded successfully for `%s`", 2, instance.action)
@@ -121,7 +122,7 @@ export default class HookManager {
121
122
  assert(isType(hook, "function"), `Hook "${event}" is not a function`, 1)
122
123
 
123
124
  const hookExecution = await hook.call(this.hooks, args)
124
- const hookTimeout = this.parent.timeout
125
+ const hookTimeout = this.timeout
125
126
  const expireAsync = () =>
126
127
  timeoutPromise(
127
128
  hookTimeout,
@@ -1,4 +1,6 @@
1
1
  import * as FDUtil from "./FDUtil.js"
2
+ import process from "node:process"
3
+ import JSON5 from "json5"
2
4
 
3
5
  const {readFile, fileExists, composeFilename} = FDUtil
4
6
 
@@ -20,7 +22,7 @@ const actionMetaRequirements = freeze({
20
22
  function loadJson(jsonFileObject) {
21
23
  // Read the file
22
24
  const jsonContent = readFile(jsonFileObject)
23
- const json = JSON.parse(jsonContent)
25
+ const json = JSON5.parse(jsonContent)
24
26
  return json
25
27
  }
26
28
 
@@ -31,11 +33,11 @@ function loadJson(jsonFileObject) {
31
33
  * @returns {object?} The parsed package.json content or null if the file does
32
34
  * not exist
33
35
  */
34
- function loadPackageJson(basePath = null) {
36
+ function loadPackageJson(basePath = process.cwd()) {
35
37
  const packageJsonFileObject = composeFilename(basePath, "./package.json")
36
38
  if(fileExists(packageJsonFileObject)) {
37
39
  const jsonContent = readFile(packageJsonFileObject)
38
- const json = JSON.parse(jsonContent)
40
+ const json = JSON5.parse(jsonContent)
39
41
  return json
40
42
  } else
41
43
  return null
@@ -1,5 +1,6 @@
1
1
  import {createRequire} from "module"
2
2
  import FDUtil from "./FDUtil.js"
3
+ import JSON5 from "json5"
3
4
 
4
5
  export default class ModuleUtil {
5
6
  /**
@@ -21,7 +22,7 @@ export default class ModuleUtil {
21
22
  static async loadJson(jsonFileObject) {
22
23
  // Read the file
23
24
  const jsonContent = await FDUtil.readFile(jsonFileObject)
24
- const json = JSON.parse(jsonContent)
25
+ const json = JSON5.parse(jsonContent)
25
26
  return json
26
27
  }
27
28
 
@@ -33,7 +34,7 @@ export default class ModuleUtil {
33
34
  static async loadPackageJson() {
34
35
  const packageJsonFileObject = FDUtil.resolveFilename("./package.json")
35
36
  const jsonContent = await FDUtil.readFile(packageJsonFileObject)
36
- const json = JSON.parse(jsonContent)
37
+ const json = JSON5.parse(jsonContent)
37
38
  return json
38
39
  }
39
40
  }