@gesslar/bedoc 1.4.10 → 1.5.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.
package/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  [![CodeQL Advanced](https://github.com/gesslar/BeDoc/actions/workflows/codeql.yml/badge.svg)](https://github.com/gesslar/BeDoc/actions/workflows/codeql.yml)
2
2
  [![Dependabot Updates](https://github.com/gesslar/BeDoc/actions/workflows/dependabot/dependabot-updates/badge.svg)](https://github.com/gesslar/BeDoc/actions/workflows/dependabot/dependabot-updates)
3
3
  [![Auto PR and Merge - dev 🤗](https://github.com/gesslar/BeDoc/actions/workflows/autopr-dev.yml/badge.svg?branch=dev)](https://github.com/gesslar/BeDoc/actions/workflows/autopr-dev.yml)
4
+ [![CodeFactor](https://www.codefactor.io/repository/github/gesslar/bedoc/badge)](https://www.codefactor.io/repository/github/gesslar/bedoc)
4
5
 
5
6
  # BeDoc
6
7
 
package/jsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "ESNext",
4
+ "moduleResolution": "Bundler",
5
+ "target": "ES2024",
6
+ "allowImportingTsExtensions": true,
7
+ "strictNullChecks": true,
8
+ "strictFunctionTypes": true
9
+ },
10
+ "exclude": [
11
+ "node_modules",
12
+ "**/node_modules/*"
13
+ ]
14
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gesslar/bedoc",
3
- "version": "1.4.10",
3
+ "version": "1.5.0",
4
4
  "description": "Pluggable documentation engine for any language and format",
5
5
  "publisher": "gesslar",
6
6
  "main": "./src/core/Core.js",
@@ -37,7 +37,6 @@
37
37
  "commander": "^13.0.0",
38
38
  "dotenv": "^16.4.7",
39
39
  "error-stack-parser": "^2.1.4",
40
- "eslint-plugin-jsdoc": "^50.6.2",
41
40
  "globby": "^14.0.2",
42
41
  "micromatch": "^4.0.8",
43
42
  "node-fetch": "^3.3.2",
@@ -49,6 +48,7 @@
49
48
  "@typescript-eslint/parser": "^8.22.0",
50
49
  "axios": "^1.7.9",
51
50
  "chokidar": "^4.0.3",
51
+ "eslint-plugin-jsdoc": "^50.6.3",
52
52
  "eslint": "^9.18.0",
53
53
  "form-data": "^4.0.1"
54
54
  },
@@ -119,7 +119,7 @@ export default class ActionManager {
119
119
  throw new Error(`No \`run\` function found for action \`${this.meta.action}\``)
120
120
 
121
121
  const actionResult = await func.call(
122
- this.action, {module: file.module, content}
122
+ this.action, {moduleName: file.module, moduleContent: content}
123
123
  )
124
124
 
125
125
  return actionResult
@@ -24,33 +24,46 @@ export default class Conveyor {
24
24
  * @returns {Promise<object>} - Resolves when all files are processed.
25
25
  */
26
26
  async convey(files, maxConcurrent = 10) {
27
- const semaphore = Array(maxConcurrent).fill(Promise.resolve())
28
-
29
- // Set up the actions
30
- await this.parse.setupAction()
31
- await this.print.setupAction()
32
-
33
- for(const file of files) {
34
- const slot = Promise.race(semaphore) // Wait for an available slot
35
- semaphore.push(slot.then(async() => {
36
- const result = await this.#processFile(file)
37
- if(result.status === "success")
38
- this.#succeeded.push({input: file, output: result.file})
39
- else if(result.status === "warning")
40
- this.#warned.push({input: file, warning: result.warning})
41
- else {
42
- this.#errored.push({input: file, error: result.error})
27
+ const fileQueue = [...files]
28
+ const activePromises = []
29
+
30
+ await Promise.all([
31
+ this.parse.setupAction(),
32
+ this.print.setupAction()
33
+ ])
34
+
35
+ const processNextFile = file => {
36
+ return this.#processFile(file).then(processedResult => {
37
+ // Store result
38
+ if(processedResult.status === "success") {
39
+ this.#succeeded.push({input: file, output: processedResult.file})
40
+ } else if(processedResult.status === "warning") {
41
+ this.#warned.push({input: file, warning: processedResult.warning})
42
+ } else {
43
+ this.#errored.push({input: file, error: processedResult.error})
43
44
  }
44
- }))
45
- semaphore.shift() // Remove the oldest promise
45
+
46
+ // Start next job if queue isn't empty
47
+ if(fileQueue.length > 0) {
48
+ const nextFile = fileQueue.shift()
49
+ return processNextFile(nextFile)
50
+ }
51
+ })
52
+ }
53
+
54
+ // Initial fill of the worker pool
55
+ while(activePromises.length < maxConcurrent && fileQueue.length > 0) {
56
+ const file = fileQueue.shift()
57
+ activePromises.push(processNextFile(file))
46
58
  }
47
59
 
48
- // Wait for all tasks to complete
49
- await Promise.all(semaphore)
60
+ // Wait for all processing to complete
61
+ await Promise.all(activePromises)
50
62
 
51
- // Clean up actions
52
- await this.parse.cleanupAction()
53
- await this.print.cleanupAction()
63
+ await Promise.all([
64
+ this.parse.cleanupAction(),
65
+ this.print.cleanupAction()
66
+ ])
54
67
 
55
68
  return {
56
69
  succeeded: this.#succeeded,
@@ -67,14 +80,13 @@ export default class Conveyor {
67
80
  */
68
81
  async #processFile(file) {
69
82
  const debug = this.logger.newDebug()
70
- const warn = (...arg) => this.logger.warn(...arg)
71
83
  const {parse, print} = this
72
84
 
73
85
  try {
74
86
  debug("Processing file: `%s`", 2, file.path)
75
87
 
76
88
  // Step 1: Read file
77
- const fileContent = await readFile(file)
89
+ const fileContent = readFile(file)
78
90
  debug("Read file content `%s` (%d bytes)", 2, file.path, fileContent.length)
79
91
 
80
92
  // Step 2: Parse file
@@ -90,8 +102,8 @@ export default class Conveyor {
90
102
  else
91
103
  debug("Parsed file successfully: `%s`", 2, file.path)
92
104
 
93
- if(!parseResult.result?.functions?.length) {
94
- const mess = format("No functions found in `%s`. No file written.", file.path)
105
+ if(!parseResult.result) {
106
+ const mess = format("No content found in `%s`. No file written.", file.path)
95
107
  return {status: "warning", file, warning: mess}
96
108
  }
97
109
 
@@ -106,21 +118,31 @@ export default class Conveyor {
106
118
  debug("Printed file successfully: `%s`", 2, file.path)
107
119
 
108
120
  // Step 4: Write output
109
- const {status: printStatus, destFile, content} = printResult
121
+ const {status: printStatus, destFile, destContent} = printResult
110
122
  const isNullish = value => value == null // Checks null or undefined
111
123
 
112
- if(printStatus !== "success" || isNullish(destFile) || isNullish(content)) {
113
- return {status: "error", file, error: new Error("Invalid print result")}
114
- } else if(!destFile || !content) {
115
- const mess = format("No content or destination file for %s", file.path)
116
- warn(mess)
117
- return {status: "warning", file, warning: mess}
124
+ switch(printStatus) {
125
+ case "warning":
126
+ case "error":
127
+ return printResult
128
+ case "success":
129
+ if(isNullish(destFile) || isNullish(destContent))
130
+ return {
131
+ status: "warning",
132
+ warning: format("No content or destination file for %s", file.path)
133
+ }
134
+
135
+ break
136
+ default:
137
+ throw new Error(`Invalid status received from printing ${file.module}`)
118
138
  }
119
139
 
120
- const writeResult = await this.#writeOutput(destFile, content)
140
+ const writeResult = await this.#writeOutput(destFile, destContent)
121
141
 
122
142
  if(writeResult.status === "success")
123
- debug("Wrote output for: `%s` (%d bytes)", 2, file.path, content.length)
143
+ debug("Wrote output for: `%s` (%d bytes)", 2, file.path, destContent.length)
144
+ else
145
+ debug("Error writing output for: `%s`", 2, file.path)
124
146
 
125
147
  return writeResult
126
148
  } catch(error) {
@@ -132,13 +154,14 @@ export default class Conveyor {
132
154
  * Writes the output to the destination.
133
155
  *
134
156
  * @param {string} destFile - Destination file path.
135
- * @param {string} content - File content.
157
+ * @param {string} destContent - File content.
136
158
  * @returns {Promise<object>} - Resolves when the file is written.
137
159
  */
138
- async #writeOutput(destFile, content) {
160
+ async #writeOutput(destFile, destContent) {
139
161
  const destFileMap = composeFilename(this.output.path, destFile)
162
+
140
163
  try {
141
- writeFile(destFileMap, content)
164
+ writeFile(destFileMap, destContent)
142
165
 
143
166
  return {status: "success", file: destFileMap}
144
167
  } catch(error) {
package/src/core/Core.js CHANGED
@@ -1,8 +1,8 @@
1
- import process from "node:process"
1
+ import {hrtime} from "node:process"
2
2
 
3
3
  import Discovery from "./Discovery.js"
4
4
  import HookManager from "./HookManager.js"
5
- import Logger, {loggerColours} from "./Logger.js"
5
+ import Logger from "./Logger.js"
6
6
  import ParseManager from "./action/ParseManager.js"
7
7
  import PrintManager from "./action/PrintManager.js"
8
8
  import Conveyor from "./Conveyor.js"
@@ -118,7 +118,7 @@ export default class Core {
118
118
  return instance
119
119
  }
120
120
 
121
- async processFiles(glob, startTime = process.hrtime()) {
121
+ async processFiles(glob) {
122
122
  const debug = this.logger.newDebug()
123
123
 
124
124
  debug("Starting file processing with conveyor", 1)
@@ -137,46 +137,23 @@ export default class Core {
137
137
  output,
138
138
  )
139
139
 
140
- const processStart = process.hrtime()
140
+ const processStart = hrtime.bigint()
141
141
 
142
142
  // Initiate the conveyor
143
- const result = await conveyor.convey(input, this.options.maxConcurrent)
143
+ const processResult = await conveyor.convey(
144
+ input, this.options.maxConcurrent
145
+ )
144
146
 
145
147
  debug("Conveyor complete", 1)
146
148
 
147
- const endTime = (process.hrtime(startTime)[1] / 1_000_000).toFixed(2)
148
- const processEnd = (process.hrtime(processStart)[1] / 1_000_000).toFixed(2)
149
-
150
- // Grab the results
151
- const totalFiles = input.length
152
- const succeeded = result.succeeded
153
- const warned = result.warned
154
- const errored = result.errored
155
-
156
- const {
157
- info: succeedColour, warn: warnColour, error: errorColour, reset
158
- } = loggerColours
159
-
160
-
161
- const success = `${succeedColour}${succeeded.length}${reset}`
162
- const warn = `${warnColour}${warned.length}${reset}`
163
- const error = `${errorColour}${errored.length}${reset}`
164
-
165
- const message = `Processed ${totalFiles} files: ${success} succeeded, ${error} errored, ` +
166
- `${warn} warned in ${processEnd}ms [total: ${endTime}ms]`
167
-
168
- this.logger.debug(message, 1)
169
-
170
- if(errored.length > 0) {
171
- // const failureRate = ((errored.length / totalFiles) * 100).toFixed(2)
172
- // const errorMessage =
173
- // `Errors processing ${errored.length} files [${failureRate}%]`
174
- // const errorLines = errored.map(r => {
175
- // const stackLine = log.lastStackLine(r.error, 0)
176
- // return `\n- ${r.input.module}: ${stackLine} - ${r.error.message}`
177
- // }).join("")
149
+ const processEnd = hrtime.bigint()
178
150
 
179
- // this.logger(errorMessage+errorLines)
151
+ const result = {
152
+ totalFiles: input.length,
153
+ succeeded: processResult.succeeded,
154
+ warned: processResult.warned,
155
+ errored: processResult.errored,
156
+ duration: ((Number(processEnd - processStart)) / 1_000_000).toFixed(2)
180
157
  }
181
158
 
182
159
  debug("File processing complete", 1)
@@ -101,10 +101,10 @@ export default class HookManager {
101
101
  * Trigger a hook
102
102
  *
103
103
  * @param {string} event - The type of hook to trigger
104
- * @param {...any} args - The hook arguments
104
+ * @param {object} args - The hook arguments as an object
105
105
  * @returns {Promise<any>} The result of the hook
106
106
  */
107
- async on(event, ...args) {
107
+ async on(event, args) {
108
108
  const debug = this.log.newDebug()
109
109
 
110
110
  debug("Triggering hook for event `%s`", 4, event)
@@ -113,18 +113,14 @@ export default class HookManager {
113
113
  throw new Error("Event type is required for hook invocation")
114
114
 
115
115
  if(!hookEvents.includes(event))
116
- throw new Error(`[HookManager.on] Invalid event type: ${event}`)
116
+ throw new Error(`Invalid event type: ${event}`)
117
117
 
118
118
  const hook = this.hooks[event]
119
119
 
120
120
  if(hook) {
121
- assert(
122
- isType(hook, "function"),
123
- `[HookManager.on] Hook "${event}" is not a function`,
124
- 1,
125
- )
121
+ assert(isType(hook, "function"), `Hook "${event}" is not a function`, 1)
126
122
 
127
- const hookExecution = await hook.call(this.hooks, ...args)
123
+ const hookExecution = await hook.call(this.hooks, args)
128
124
  const hookTimeout = this.parent.timeout
129
125
  const expireAsync = () =>
130
126
  timeoutPromise(
@@ -139,6 +135,8 @@ export default class HookManager {
139
135
  debug("Hook executed successfully for event: `%s`", 4, event)
140
136
 
141
137
  return result
138
+ } else {
139
+ return null
142
140
  }
143
141
  }
144
142
  }