@gesslar/bedoc 1.1.0 → 1.3.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 (49) hide show
  1. package/dist/types/cli.d.ts +3 -0
  2. package/dist/types/cli.d.ts.map +10 -0
  3. package/dist/types/core/ActionManager.d.ts +27 -0
  4. package/dist/types/core/ActionManager.d.ts.map +10 -0
  5. package/dist/types/core/Configuration.d.ts +27 -0
  6. package/dist/types/core/Configuration.d.ts.map +10 -0
  7. package/dist/types/core/ConfigurationParameters.d.ts +38 -0
  8. package/dist/types/core/ConfigurationParameters.d.ts.map +10 -0
  9. package/dist/types/core/Conveyor.d.ts +47 -0
  10. package/dist/types/core/Conveyor.d.ts.map +10 -0
  11. package/dist/types/core/Core.d.ts +53 -0
  12. package/dist/types/core/Core.d.ts.map +10 -0
  13. package/dist/types/core/Discovery.d.ts +73 -0
  14. package/dist/types/core/Discovery.d.ts.map +10 -0
  15. package/dist/types/core/HookManager.d.ts +60 -0
  16. package/dist/types/core/HookManager.d.ts.map +10 -0
  17. package/dist/types/core/Logger.d.ts +55 -0
  18. package/dist/types/core/Logger.d.ts.map +10 -0
  19. package/dist/types/core/action/ParseManager.d.ts +8 -0
  20. package/dist/types/core/action/ParseManager.d.ts.map +10 -0
  21. package/dist/types/core/action/PrintManager.d.ts +8 -0
  22. package/dist/types/core/action/PrintManager.d.ts.map +10 -0
  23. package/dist/types/core/util/ActionUtil.d.ts +35 -0
  24. package/dist/types/core/util/ActionUtil.d.ts.map +10 -0
  25. package/dist/types/core/util/DataUtil.d.ts +52 -0
  26. package/dist/types/core/util/DataUtil.d.ts.map +10 -0
  27. package/dist/types/core/util/FDUtil.d.ts +146 -0
  28. package/dist/types/core/util/FDUtil.d.ts.map +10 -0
  29. package/dist/types/core/util/ModuleUtil.d.ts +27 -0
  30. package/dist/types/core/util/ModuleUtil.d.ts.map +10 -0
  31. package/dist/types/core/util/StringUtil.d.ts +5 -0
  32. package/dist/types/core/util/StringUtil.d.ts.map +10 -0
  33. package/dist/types/core/util/TypeSpec.d.ts +42 -0
  34. package/dist/types/core/util/TypeSpec.d.ts.map +10 -0
  35. package/dist/types/core/util/ValidUtil.d.ts +29 -0
  36. package/dist/types/core/util/ValidUtil.d.ts.map +10 -0
  37. package/package.json +10 -3
  38. package/src/cli.js +12 -11
  39. package/src/core/ActionManager.js +85 -22
  40. package/src/core/Conveyor.js +36 -16
  41. package/src/core/Core.js +68 -78
  42. package/src/core/Discovery.js +219 -65
  43. package/src/core/{HooksManager.js → HookManager.js} +14 -13
  44. package/src/core/Logger.js +3 -11
  45. package/src/core/action/ParseManager.js +0 -19
  46. package/src/core/action/PrintManager.js +0 -19
  47. package/src/core/util/DataUtil.js +3 -1
  48. package/src/core/util/FDUtil.js +1 -1
  49. package/tsconfig.json +20 -0
@@ -0,0 +1,146 @@
1
+ export type FDType = 'file' | 'directory';
2
+
3
+ export interface FileMap {
4
+ path: string;
5
+ uri: string;
6
+ absolutePath: string;
7
+ absoluteUri: string;
8
+ name: string;
9
+ module: string;
10
+ extension: string;
11
+ isFile: true;
12
+ isDirectory: false;
13
+ directory?: DirMap;
14
+ }
15
+
16
+ export interface DirMap {
17
+ path: string;
18
+ uri: string;
19
+ absolutePath: string;
20
+ absoluteUri: string;
21
+ name: string;
22
+ separator: string;
23
+ isFile: false;
24
+ isDirectory: true;
25
+ }
26
+
27
+ export interface FilenameParts {
28
+ basename: string;
29
+ dirname: string;
30
+ extname: string;
31
+ }
32
+
33
+ export const fdType: Readonly<Record<Uppercase<FDType>, FDType>>;
34
+ export const fdTypes: readonly FDType[];
35
+
36
+ /**
37
+ * Compose a directory map from a path
38
+ *
39
+ * @param {string} directory - The directory
40
+ * @returns {DirMap} A directory object
41
+ */
42
+ export function composeDirectory(directory: string): DirMap;
43
+ /**
44
+ * Compose a file path from a directory and a file
45
+ *
46
+ * @param {string|DirMap} directoryNameorObject - The directory
47
+ * @param {string} fileName - The file
48
+ * @returns {FileMap} A file object
49
+ */
50
+ export function composeFilename(directoryNameorObject: string | DirMap, fileName: string): FileMap;
51
+ /**
52
+ * Deconstruct a filename into parts
53
+ *
54
+ * @param {string} fileName - The filename to deconstruct
55
+ * @returns {FilenameParts} The filename parts
56
+ */
57
+ export function deconstructFilenameToParts(fileName: string): FilenameParts;
58
+ /**
59
+ * Fix slashes in a path
60
+ *
61
+ * @param {string} pathName - The path to fix
62
+ * @returns {string} The fixed path
63
+ */
64
+ export function fixSlashes(pathName: string): string;
65
+ /**
66
+ * Retrieve all files matching a specific glob pattern.
67
+ *
68
+ * @param {string|string[]} globPattern - The glob pattern(s) to search.
69
+ * @returns {Promise<FileMap[]>} An array of file objects
70
+ * @throws {Error} Throws an error for invalid input or search failure.
71
+ */
72
+ export function getFiles(globPattern: string | string[]): Promise<FileMap[]>;
73
+ /**
74
+ * Lists the contents of a directory.
75
+ *
76
+ * @param {string} directory - The directory to list.
77
+ * @returns {Promise<{files: FileMap[], directories: DirMap[]}>} The files and
78
+ * directories in the directory.
79
+ */
80
+ export function ls(directory: string): Promise<{
81
+ files: FileMap[];
82
+ directories: DirMap[];
83
+ }>;
84
+ /**
85
+ * Map a directory to a DirMap
86
+ *
87
+ * @param {string} directoryName - The directory to map
88
+ * @returns {DirMap} A directory object
89
+ */
90
+ export function mapDirectory(directoryName: string): DirMap;
91
+ /**
92
+ * Map a file to a FileMap
93
+ *
94
+ * @param {string} fileName - The file to map
95
+ * @returns {FileMap} A file object
96
+ */
97
+ export function mapFilename(fileName: string): FileMap;
98
+ /**
99
+ * Convert a path to a URI
100
+ *
101
+ * @param {string} pathName - The path to convert
102
+ * @returns {string} The URI
103
+ * @throws {Error} If the path is not a valid file path
104
+ */
105
+ export function pathToUri(pathName: string): string;
106
+ /**
107
+ * Reads the content of a file synchronously.
108
+ *
109
+ * @param {FileMap} fileObject - The file map containing the file path
110
+ * @returns {string} The file contents
111
+ */
112
+ export function readFile(fileObject: FileMap): string;
113
+ /**
114
+ * Resolves a path to an absolute path
115
+ *
116
+ * @param {string} directoryName - The path to resolve
117
+ * @returns {DirMap} The directory object
118
+ * @throws {Error}
119
+ */
120
+ export function resolveDirectory(directoryName: string): DirMap;
121
+ /**
122
+ * Resolves a file to an absolute path
123
+ *
124
+ * @param {string} fileName - The file to resolve
125
+ * @param {DirMap} [directoryObject] - The directory object to resolve the
126
+ * file in
127
+ * @returns {FileMap} A file object (validated)
128
+ * @throws {Error}
129
+ */
130
+ export function resolveFilename(fileName: string, directoryObject?: DirMap | null): FileMap;
131
+ /**
132
+ * Convert a URI to a path
133
+ *
134
+ * @param {string} pathName - The URI to convert
135
+ * @returns {string} The path
136
+ * @throws {Error} If the URI is not a valid file URL
137
+ */
138
+ export function uriToPath(pathName: string): string;
139
+ /**
140
+ * Writes content to a file synchronously.
141
+ *
142
+ * @param {FileMap} fileObject - The file map containing the file path
143
+ * @param {string} content - The content to write
144
+ */
145
+ export function writeFile(fileObject: FileMap, content: string): void;
146
+ //# sourceMappingURL=FDUtil.d.ts.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "file": "FDUtil.d.ts",
4
+ "sourceRoot": "",
5
+ "sources": [
6
+ "../../../../src/core/util/FDUtil.js"
7
+ ],
8
+ "names": [],
9
+ "mappings": "AAgBA,yBAAkE;AAFlE,0BAA6C;AA0N7C;;;;;GAKG;AACH,4CAHW,MAAM,GACJ,MAAM,CAIlB;AAjJD;;;;;;GAMG;AACH,uDAJW,MAAM,GAAC,MAAM,YACb,MAAM,GACJ,MAAM,CAiBlB;AAyCD;;;;;GAKG;AACH,qDAHW,MAAM,GACJ,MAAM,CAMlB;AAtJD;;;;;GAKG;AACH,qCAHW,MAAM,GACJ,MAAM,CAIlB;AAgJD;;;;;;GAMG;AACH,sCAJW,MAAM,GAAC,MAAM,EAAE,GACb,OAAO,CAAC,MAAM,EAAE,CAAC,CA+B7B;AAqCD;;;;;;GAMG;AACH,8BAJW,MAAM,GACJ,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,WAAW,EAAE,MAAM,EAAE,CAAA;CAAC,CAAC,CAsB7D;AAjID;;;;;GAKG;AACH,4CAHW,MAAM,GACJ,MAAM,CAalB;AArCD;;;;;GAKG;AACH,sCAHW,MAAM,GACJ,MAAM,CAclB;AA7GD;;;;;;GAMG;AACH,oCAJW,MAAM,GACJ,MAAM,CAUlB;AAoOD;;;;;GAKG;AACH,qCAHW,MAAM,GACJ,OAAO,CAAC,MAAM,CAAC,CAW3B;AA9ED;;;;;;GAMG;AACH,gDAJW,MAAM,GACJ,MAAM,CAmBlB;AA1KD;;;;;;;;GAQG;AACH,0CANW,MAAM,oBACN,MAAM,GAEJ,MAAM,CA2BlB;AAjDD;;;;;;GAMG;AACH,oCAJW,MAAM,GACJ,MAAM,CAUlB;AAqOD;;;;;GAKG;AACH,sCAHW,MAAM,WACN,MAAM,QAShB"
10
+ }
@@ -0,0 +1,27 @@
1
+ import { FileMap } from './FDUtil';
2
+
3
+ export default class ModuleUtil {
4
+ /**
5
+ * Requires a module synchronously
6
+ *
7
+ * @param {FileMap} fileObject - The file to require
8
+ * @returns {unknown} The required module
9
+ */
10
+ static require(fileObject: FileMap): unknown;
11
+
12
+ /**
13
+ * Loads a JSON file asynchronously
14
+ *
15
+ * @param {FileMap} jsonFileObject - The JSON file to load
16
+ * @returns {Promise<Record<string, unknown>>} The parsed JSON content
17
+ */
18
+ static loadJson(jsonFileObject: FileMap): Promise<Record<string, unknown>>;
19
+
20
+ /**
21
+ * Loads the package.json file asynchronously
22
+ *
23
+ * @returns {Promise<Record<string, unknown>>} The parsed package.json content
24
+ */
25
+ static loadPackageJson(): Promise<Record<string, unknown>>;
26
+ }
27
+ //# sourceMappingURL=ModuleUtil.d.ts.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "file": "ModuleUtil.d.ts",
4
+ "sourceRoot": "",
5
+ "sources": [
6
+ "../../../../src/core/util/ModuleUtil.js"
7
+ ],
8
+ "names": [],
9
+ "mappings": "AAGA;IACE;;;;;OAKG;IACH,2BAHW,MAAM,GACJ,MAAM,CAIlB;IAED;;;;;OAKG;IACH,gCAHW,MAAM,GACJ,OAAO,CAAC,MAAM,CAAC,CAO3B;IAED;;;;OAIG;IACH,0BAFa,OAAO,CAAC,MAAM,CAAC,CAO3B;CACF"
10
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Capitalizes the first letter of a string
3
+ */
4
+ export function capitalize(str: string): string;
5
+ //# sourceMappingURL=StringUtil.d.ts.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "file": "StringUtil.d.ts",
4
+ "sourceRoot": "",
5
+ "sources": [
6
+ "../../../../src/core/util/StringUtil.js"
7
+ ],
8
+ "names": [],
9
+ "mappings": "AAAA;;;;;GAKG;AACH,gCAHW,MAAM,GACJ,MAAM,CAIlB"
10
+ }
@@ -0,0 +1,42 @@
1
+ import { DataType } from './DataUtil';
2
+
3
+ interface TypeSpecification {
4
+ typeName: DataType;
5
+ array: boolean;
6
+ }
7
+
8
+ interface TypeSpecOptions {
9
+ delimiter?: string;
10
+ allowEmpty?: boolean;
11
+ }
12
+
13
+ interface TypeSpecJSON {
14
+ specs: TypeSpecification[];
15
+ length: number;
16
+ stringRepresentation: string;
17
+ }
18
+
19
+ export default class TypeSpec {
20
+ constructor(typeString: string, options?: TypeSpecOptions);
21
+
22
+ readonly specs: readonly TypeSpecification[];
23
+ readonly length: number;
24
+ readonly stringRepresentation: string;
25
+
26
+ toString(): string;
27
+ toJSON(): TypeSpecJSON;
28
+
29
+ forEach(callback: (spec: TypeSpecification) => void): void;
30
+ every(callback: (spec: TypeSpecification) => boolean): boolean;
31
+ some(callback: (spec: TypeSpecification) => boolean): boolean;
32
+ filter(callback: (spec: TypeSpecification) => boolean): TypeSpecification[];
33
+ map<T>(callback: (spec: TypeSpecification) => T): T[];
34
+ reduce<T>(callback: (acc: T, spec: TypeSpecification) => T, initialValue: T): T;
35
+ find(callback: (spec: TypeSpecification) => boolean): TypeSpecification | undefined;
36
+
37
+ match(value: unknown, options?: TypeSpecOptions): boolean;
38
+
39
+ #specs: TypeSpecification[];
40
+ #parse(typeString: string, options?: TypeSpecOptions): void;
41
+ }
42
+ //# sourceMappingURL=TypeSpec.d.ts.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "file": "TypeSpec.d.ts",
4
+ "sourceRoot": "",
5
+ "sources": [
6
+ "../../../../src/core/util/TypeSpec.js"
7
+ ],
8
+ "names": [],
9
+ "mappings": "AAIA;IAGE,uCAQC;IAJC,aAAwB;IACxB,eAAgC;IAChC,6BAA2C;IAI7C,mBAMC;IAED;;;;MAOC;IAED,6BAEC;IACD,8BAEC;IACD,6BAEC;IACD,6BAEC;IACD,0BAEC;IACD,8CAEC;IACD,yBAEC;IAED,yCAqCC;;CAoBF"
10
+ }
@@ -0,0 +1,29 @@
1
+ import TypeSpec from './TypeSpec';
2
+
3
+ interface ValidTypeOptions {
4
+ allowEmpty?: boolean;
5
+ }
6
+
7
+ /**
8
+ * Validates a value against a type
9
+ *
10
+ * @throws {Error} If the value does not match the expected type
11
+ */
12
+ export function validType(
13
+ value: unknown,
14
+ type: string | TypeSpec,
15
+ options?: ValidTypeOptions,
16
+ depth?: number
17
+ ): void;
18
+
19
+ /**
20
+ * Asserts a condition
21
+ *
22
+ * @throws {Error} If the condition is not met, with optional argument in message
23
+ */
24
+ export function assert(
25
+ condition: boolean,
26
+ message: string,
27
+ arg?: number | null
28
+ ): asserts condition;
29
+ //# sourceMappingURL=ValidUtil.d.ts.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "file": "ValidUtil.d.ts",
4
+ "sourceRoot": "",
5
+ "sources": [
6
+ "../../../../src/core/util/ValidUtil.js"
7
+ ],
8
+ "names": [],
9
+ "mappings": "AAsBA;;;;;;;;GAQG;AACH,kCANW,OAAO,WACP,MAAM,QAEN,MAAM,QAmBhB;AAzCD;;;;;;;GAOG;AACH,iCALW,GAAC,QACD,MAAM,YAEN,MAAM,QAQhB"
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gesslar/bedoc",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "Pluggable documentation engine for any language and format",
5
5
  "publisher": "gesslar",
6
6
  "main": "./src/core/Core.js",
@@ -14,12 +14,20 @@
14
14
  "bin": {
15
15
  "bedoc": "src/cli.js"
16
16
  },
17
+ "exports": {
18
+ ".": {
19
+ "import": "./src/core/Core.js",
20
+ "types": "./dist/types/core/Core.d.ts"
21
+ }
22
+ },
17
23
  "type": "module",
24
+ "types": "dist/types/index.d.ts",
18
25
  "scripts": {
19
26
  "lint:check": "npx eslint .",
20
27
  "lint:fix": "npx eslint . --fix"
21
28
  },
22
29
  "dependencies": {
30
+ "@gesslar/bedoc": "^1.2.0",
23
31
  "commander": "^13.0.0",
24
32
  "dotenv": "^16.4.7",
25
33
  "error-stack-parser": "^2.1.4",
@@ -27,11 +35,10 @@
27
35
  "globby": "^14.0.2",
28
36
  "micromatch": "^4.0.8",
29
37
  "node-fetch": "^3.3.2",
30
- "vscode-uri": "^3.0.8",
31
38
  "yaml": "^2.7.0"
32
39
  },
33
40
  "devDependencies": {
34
- "@stylistic/eslint-plugin-js": "^2.13.0",
41
+ "@stylistic/eslint-plugin-js": "^3.0.0",
35
42
  "axios": "^1.7.9",
36
43
  "chokidar": "^4.0.3",
37
44
  "eslint": "^9.18.0",
package/src/cli.js CHANGED
@@ -63,7 +63,7 @@ const {resolveDirectory} = FDUtil
63
63
  }
64
64
 
65
65
  // Create core instance with validated config
66
- BeDoc
66
+ const bedoc = await BeDoc
67
67
  .new({
68
68
  options: {
69
69
  ...optionsWithSources,
@@ -72,19 +72,20 @@ const {resolveDirectory} = FDUtil
72
72
  },
73
73
  source: Environment.CLI
74
74
  })
75
- } catch(e) {
76
- if(e instanceof Error) {
77
- if(e instanceof AggregateError) {
78
- for(const error of e.errors) {
79
- console.error(`Error: ${error.message}`)
80
- }
81
- } else if(e.stack) {
82
- console.error(e.stack)
75
+ const filesToProcess = bedoc.options.input.map(f => f.absolutePath)
76
+ const result = await bedoc.processFiles(filesToProcess)
77
+ const errored = result.errored
78
+ if(errored.length > 0)
79
+ throw new AggregateError(errored.map(e => e.error), "Error processing files")
80
+ } catch(error) {
81
+ if(error instanceof Error) {
82
+ if(error instanceof AggregateError) {
83
+ error.errors.forEach(e => console.error(e))
83
84
  } else {
84
- console.error(`Error: ${e.message}`)
85
+ console.error(error.message, error.stack)
85
86
  }
86
87
  } else {
87
- console.error(`Error: ${e}`)
88
+ console.error("Error: %o", error)
88
89
  }
89
90
 
90
91
  process.exit(1)
@@ -1,27 +1,26 @@
1
- import {hookPoints} from "./HooksManager.js"
1
+ import {hookPoints} from "./HookManager.js"
2
2
 
3
3
  export default class ActionManager {
4
4
  #action = null
5
- #meta = {}
6
- #hooks = null
5
+ #hookManager = null
7
6
  #contract
8
- #module
9
7
  #log
10
8
  #debug
9
+ #file
11
10
 
12
11
  constructor(actionDefinition, logger) {
13
12
  this.#log = logger
14
13
  this.#debug = this.#log.newDebug()
15
14
 
16
- this.#setupAction(actionDefinition)
15
+ this.#initialize(actionDefinition)
17
16
  }
18
17
 
19
- #setupAction(actionDefinition) {
18
+ #initialize(actionDefinition) {
20
19
  const debug = this.#debug
21
20
 
22
21
  debug("Setting up action", 2)
23
22
 
24
- const {action, contract, module, meta} = actionDefinition
23
+ const {action, file, contract} = actionDefinition
25
24
 
26
25
  if(!action)
27
26
  throw new Error("Action is required")
@@ -29,13 +28,9 @@ export default class ActionManager {
29
28
  if(!contract)
30
29
  throw new Error("Contract is required")
31
30
 
32
- if(!module)
33
- throw new Error("Module is required")
34
-
35
- this.#module = module
36
31
  this.#action = action
37
32
  this.#contract = contract
38
- this.#meta = meta
33
+ this.#file = file
39
34
 
40
35
  debug("Action setup complete", 2)
41
36
  }
@@ -44,33 +39,101 @@ export default class ActionManager {
44
39
  return this.#action
45
40
  }
46
41
 
47
- get hooks() {
48
- return this.#hooks
42
+ get hookManager() {
43
+ return this.#hookManager
49
44
  }
50
45
 
51
- set hooks(hookManager) {
52
- if(this.hooks)
46
+ set hookManager(hookManager) {
47
+ if(this.hookManager)
53
48
  throw new Error("Hooks already set")
54
49
 
55
50
  this.action.hook = hookManager.on.bind(this.action)
56
51
  this.action.HOOKS = hookPoints
52
+ this.#hookManager = hookManager
57
53
  this.action.hooks = hookManager.hooks
58
- this.#hooks = hookManager
59
54
  }
60
55
 
61
56
  get contract() {
62
57
  return this.#contract
63
58
  }
64
59
 
65
- get module() {
66
- return this.#module
67
- }
68
-
69
60
  get meta() {
70
- return this.#meta
61
+ return this.#action.meta
71
62
  }
72
63
 
73
64
  get log() {
74
65
  return this.#log
75
66
  }
67
+
68
+ async #setupAction() {
69
+ const setup = this.action?.setup
70
+
71
+ if(!setup)
72
+ return
73
+
74
+ await this.action.setup.call(
75
+ this.action, {parent: this, log: this.#log}
76
+ )
77
+ }
78
+
79
+ async #cleanupAction() {
80
+ const cleanup = this.action?.cleanup
81
+
82
+ if(!cleanup)
83
+ return
84
+
85
+ await this.action.cleanup.call(this.action)
86
+ }
87
+
88
+ async #setupHooks() {
89
+ const setup = this.hookManager?.setup
90
+
91
+ if(!setup)
92
+ return
93
+
94
+ await this.hookManager.setup.call(
95
+ this.hookManager.hooks, {parent: this.action, log: this.#log}
96
+ )
97
+ }
98
+
99
+ async #cleanupHooks() {
100
+ const cleanup = this.hookManager?.cleanup
101
+
102
+ if(!cleanup)
103
+ return
104
+
105
+ await this.hookManager.cleanup.call(this.hookManager.hooks)
106
+ }
107
+
108
+ async setupAction() {
109
+ this.#debug("Setting up action for %s", 2, this.meta.action)
110
+
111
+ await this.#setupHooks()
112
+ await this.#setupAction()
113
+ }
114
+
115
+ async runAction({file,content}) {
116
+ const func = this.action.run
117
+
118
+ if(!func)
119
+ throw new Error(`No \`run\` function found for action \`${this.meta.action}\``)
120
+
121
+ const actionResult = await func.call(
122
+ this.action, {module: file.module, content}
123
+ )
124
+
125
+ return actionResult
126
+ }
127
+
128
+ async cleanupAction() {
129
+ this.#debug("Post action", 2)
130
+ this.#debug("Cleaning up action for %s", 2, this.meta.action)
131
+
132
+ await this.#cleanupHooks()
133
+ await this.#cleanupAction()
134
+ }
135
+
136
+ toString() {
137
+ return `${this.#file?.module || "UNDEFINED"} (${this.meta?.action || "UNDEFINED"})`
138
+ }
76
139
  }
@@ -1,3 +1,5 @@
1
+ import {format} from "node:util"
2
+
1
3
  import * as FDUtil from "./util/FDUtil.js"
2
4
 
3
5
  const {readFile, writeFile, composeFilename} = FDUtil
@@ -6,9 +8,9 @@ export default class Conveyor {
6
8
  #succeeded = []
7
9
  #errored = []
8
10
 
9
- constructor(parser, printer, logger, output) {
10
- this.parser = parser
11
- this.printer = printer
11
+ constructor(parse, print, logger, output) {
12
+ this.parse = parse
13
+ this.print = print
12
14
  this.logger = logger
13
15
  this.output = output
14
16
  }
@@ -23,15 +25,19 @@ export default class Conveyor {
23
25
  async convey(files, maxConcurrent = 10) {
24
26
  const semaphore = Array(maxConcurrent).fill(Promise.resolve())
25
27
 
28
+ // Set up the actions
29
+ await this.parse.setupAction()
30
+ await this.print.setupAction()
31
+
26
32
  for(const file of files) {
27
33
  const slot = Promise.race(semaphore) // Wait for an available slot
28
34
  semaphore.push(slot.then(async() => {
29
35
  const result = await this.#processFile(file)
30
-
31
- if(result.status === "success")
36
+ if(result.status === "success" || result.status === "warning")
32
37
  this.#succeeded.push({input: file, output: result.file})
33
- else
38
+ else {
34
39
  this.#errored.push({input: file, error: result.error})
40
+ }
35
41
  }))
36
42
  semaphore.shift() // Remove the oldest promise
37
43
  }
@@ -39,6 +45,10 @@ export default class Conveyor {
39
45
  // Wait for all tasks to complete
40
46
  await Promise.all(semaphore)
41
47
 
48
+ // Clean up actions
49
+ await this.parse.cleanupAction()
50
+ await this.print.cleanupAction()
51
+
42
52
  return {succeeded: this.#succeeded, errored: this.#errored}
43
53
  }
44
54
 
@@ -50,6 +60,8 @@ export default class Conveyor {
50
60
  */
51
61
  async #processFile(file) {
52
62
  const debug = this.logger.newDebug()
63
+ const warn = (...arg) => this.logger.warn(...arg)
64
+ const {parse, print} = this
53
65
 
54
66
  try {
55
67
  debug("Processing file: `%s`", 2, file.path)
@@ -59,26 +71,36 @@ export default class Conveyor {
59
71
  debug("Read file content `%s` (%d bytes)", 2, file.path, fileContent.length)
60
72
 
61
73
  // Step 2: Parse file
62
- const parseResult = await this.parser.parse(file, fileContent)
74
+ const parseResult = await parse.runAction({
75
+ file,
76
+ content: fileContent
77
+ })
63
78
  if(parseResult.status === "error")
64
79
  return parseResult
65
80
 
66
81
  debug("Parsed file successfully: `%s`", 2, file.path)
67
82
 
68
83
  // Step 3: Print file
69
- const printResult = await this.printer.print(
84
+ const printResult = await print.runAction({
70
85
  file,
71
- parseResult.result,
72
- )
86
+ content: parseResult.result,
87
+ })
73
88
  if(printResult.status === "error")
74
89
  return printResult
75
90
 
76
91
  debug("Printed file successfully: `%s`", 2, file.path)
77
92
 
78
93
  // Step 4: Write output
79
- const {destFile, content} = printResult
80
- if(!destFile || !content)
81
- return {status: "error", message: "Invalid print result"}
94
+ const {status: printStatus, destFile, content} = printResult
95
+ const isNullish = (value) => value == null // Checks null or undefined
96
+
97
+ if(printStatus !== "success" || isNullish(destFile) || isNullish(content)) {
98
+ return {status: "error", file, error: new Error("Invalid print result")}
99
+ } else if(!destFile || !content) {
100
+ const mess = format("No content or destination file for %s", file.path)
101
+ warn(mess)
102
+ return {status: "warning", file, warning: mess}
103
+ }
82
104
 
83
105
  const writeResult = await this.#writeOutput(destFile, content)
84
106
 
@@ -87,9 +109,7 @@ export default class Conveyor {
87
109
 
88
110
  return writeResult
89
111
  } catch(error) {
90
- const mess = `Error processing file ${file.path}: ${error.message}\n${error.stack}`
91
- this.logger.error(mess)
92
- return {status: "error", error}
112
+ return {status: "error", file, error}
93
113
  }
94
114
  }
95
115