@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.
- package/dist/types/cli.d.ts +3 -0
- package/dist/types/cli.d.ts.map +10 -0
- package/dist/types/core/ActionManager.d.ts +27 -0
- package/dist/types/core/ActionManager.d.ts.map +10 -0
- package/dist/types/core/Configuration.d.ts +27 -0
- package/dist/types/core/Configuration.d.ts.map +10 -0
- package/dist/types/core/ConfigurationParameters.d.ts +38 -0
- package/dist/types/core/ConfigurationParameters.d.ts.map +10 -0
- package/dist/types/core/Conveyor.d.ts +47 -0
- package/dist/types/core/Conveyor.d.ts.map +10 -0
- package/dist/types/core/Core.d.ts +53 -0
- package/dist/types/core/Core.d.ts.map +10 -0
- package/dist/types/core/Discovery.d.ts +73 -0
- package/dist/types/core/Discovery.d.ts.map +10 -0
- package/dist/types/core/HookManager.d.ts +60 -0
- package/dist/types/core/HookManager.d.ts.map +10 -0
- package/dist/types/core/Logger.d.ts +55 -0
- package/dist/types/core/Logger.d.ts.map +10 -0
- package/dist/types/core/action/ParseManager.d.ts +8 -0
- package/dist/types/core/action/ParseManager.d.ts.map +10 -0
- package/dist/types/core/action/PrintManager.d.ts +8 -0
- package/dist/types/core/action/PrintManager.d.ts.map +10 -0
- package/dist/types/core/util/ActionUtil.d.ts +35 -0
- package/dist/types/core/util/ActionUtil.d.ts.map +10 -0
- package/dist/types/core/util/DataUtil.d.ts +52 -0
- package/dist/types/core/util/DataUtil.d.ts.map +10 -0
- package/dist/types/core/util/FDUtil.d.ts +146 -0
- package/dist/types/core/util/FDUtil.d.ts.map +10 -0
- package/dist/types/core/util/ModuleUtil.d.ts +27 -0
- package/dist/types/core/util/ModuleUtil.d.ts.map +10 -0
- package/dist/types/core/util/StringUtil.d.ts +5 -0
- package/dist/types/core/util/StringUtil.d.ts.map +10 -0
- package/dist/types/core/util/TypeSpec.d.ts +42 -0
- package/dist/types/core/util/TypeSpec.d.ts.map +10 -0
- package/dist/types/core/util/ValidUtil.d.ts +29 -0
- package/dist/types/core/util/ValidUtil.d.ts.map +10 -0
- package/package.json +10 -3
- package/src/cli.js +12 -11
- package/src/core/ActionManager.js +85 -22
- package/src/core/Conveyor.js +36 -16
- package/src/core/Core.js +68 -78
- package/src/core/Discovery.js +219 -65
- package/src/core/{HooksManager.js → HookManager.js} +14 -13
- package/src/core/Logger.js +3 -11
- package/src/core/action/ParseManager.js +0 -19
- package/src/core/action/PrintManager.js +0 -19
- package/src/core/util/DataUtil.js +3 -1
- package/src/core/util/FDUtil.js +1 -1
- package/tsconfig.json +20 -0
package/src/core/Core.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import process from "node:process"
|
|
2
2
|
|
|
3
3
|
import Discovery from "./Discovery.js"
|
|
4
|
-
import
|
|
4
|
+
import HookManager from "./HookManager.js"
|
|
5
5
|
import Logger from "./Logger.js"
|
|
6
6
|
import ParseManager from "./action/ParseManager.js"
|
|
7
7
|
import PrintManager from "./action/PrintManager.js"
|
|
@@ -33,100 +33,85 @@ export default class Core {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
static async new({options, source}) {
|
|
36
|
-
const
|
|
36
|
+
const config = new Configuration()
|
|
37
37
|
|
|
38
|
-
const
|
|
39
|
-
if(
|
|
40
|
-
throw new AggregateError(
|
|
38
|
+
const validConfig = await config.validate({options, source})
|
|
39
|
+
if(validConfig.status === "error")
|
|
40
|
+
throw new AggregateError(validConfig.errors,"BeDoc configuration failed")
|
|
41
41
|
|
|
42
|
-
const instance = new Core({...
|
|
42
|
+
const instance = new Core({...validConfig, name: "BeDoc"})
|
|
43
43
|
const debug = instance.logger.newDebug()
|
|
44
44
|
|
|
45
|
-
debug("Creating new BeDoc instance with options: `%o`", 2,
|
|
45
|
+
debug("Creating new BeDoc instance with options: `%o`", 2, validConfig)
|
|
46
46
|
|
|
47
47
|
const discovery = new Discovery(instance)
|
|
48
|
-
const
|
|
48
|
+
const {printer: validPrint, parser: validParse} = validConfig
|
|
49
49
|
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
50
|
+
const actionDefs = await discovery.discoverActions({
|
|
51
|
+
print: validPrint,
|
|
52
|
+
parse: validParse
|
|
53
|
+
})
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
(a) => a.action.meta[criterion] === validatedConfig[criterion],
|
|
59
|
-
)
|
|
60
|
-
}
|
|
61
|
-
}
|
|
55
|
+
const validCrit = discovery.satisfyCriteria(actionDefs, validConfig)
|
|
56
|
+
|
|
57
|
+
debug("Actions that met criteria: `%o`", 2, validCrit)
|
|
62
58
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
for(const printer of filteredActions.print) {
|
|
66
|
-
for(const parser of filteredActions.parse) {
|
|
67
|
-
const satisfied = schemaCompare(parser.contract, printer.contract)
|
|
59
|
+
if(Object.values(validCrit).some(arr => arr.length === 0))
|
|
60
|
+
throw new Error("No found matching parser and printer")
|
|
68
61
|
|
|
69
|
-
|
|
70
|
-
|
|
62
|
+
const validSchemas = {print: [], parse: []}
|
|
63
|
+
let printers = validCrit.print.length
|
|
64
|
+
while(printers--) {
|
|
65
|
+
const printer = validCrit.print[printers]
|
|
66
|
+
const printerSchema = printer.contract
|
|
67
|
+
const satisfied = []
|
|
68
|
+
for(const parser of validCrit.parse) {
|
|
69
|
+
const parserSchema = parser.contract
|
|
70
|
+
const result = schemaCompare(parserSchema, printerSchema)
|
|
71
|
+
if(result.status === "success")
|
|
72
|
+
satisfied.push(parser)
|
|
71
73
|
}
|
|
72
|
-
}
|
|
73
74
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
`${matches.map((m) => m.print.name).join(", ")}`
|
|
79
|
-
throw new Error(message)
|
|
75
|
+
if(satisfied.length > 0) {
|
|
76
|
+
validSchemas.print.push(printer)
|
|
77
|
+
validSchemas.parse.push(...satisfied)
|
|
78
|
+
}
|
|
80
79
|
}
|
|
81
80
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
const finalActions = {}
|
|
82
|
+
for(const [key, value] of Object.entries(validSchemas)) {
|
|
83
|
+
if(value.length === 0)
|
|
84
|
+
throw new Error(`No matching ${key} found`)
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const satisfied = schemaCompare(
|
|
90
|
-
chosenActions.parse.contract,
|
|
91
|
-
chosenActions.print.contract,
|
|
92
|
-
)
|
|
86
|
+
if(value.length > 1)
|
|
87
|
+
throw new Error(`Multiple matching ${key} found`)
|
|
93
88
|
|
|
94
|
-
|
|
95
|
-
instance.logger.error(
|
|
96
|
-
`[Core.new] action contract failed: ${satisfied.errors}`,
|
|
97
|
-
)
|
|
98
|
-
throw new AggregateError(satisfied.errors, "Action contract failed")
|
|
99
|
-
} else if(satisfied.status !== "success") {
|
|
100
|
-
throw new Error(
|
|
101
|
-
`[Core.new] Action contract failed: ${satisfied.message}`,
|
|
102
|
-
)
|
|
89
|
+
finalActions[key] = validSchemas[key][0]
|
|
103
90
|
}
|
|
104
91
|
|
|
105
92
|
debug("Contracts satisfied between parser and printer", 2)
|
|
106
93
|
|
|
107
94
|
// Adding to instance
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
action: action,
|
|
123
|
-
hooksFile: validatedConfig.hooks,
|
|
95
|
+
instance.actions = {}
|
|
96
|
+
const managers = {print: PrintManager, parse: ParseManager}
|
|
97
|
+
for(const [, value] of Object.entries(finalActions)) {
|
|
98
|
+
const {action: actionType} = value.action.meta
|
|
99
|
+
|
|
100
|
+
debug("Attaching `%o` action to instance", 2, actionType)
|
|
101
|
+
instance.actions[actionType] = new managers[actionType](
|
|
102
|
+
value, instance.logger
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
if(validConfig.hooks) {
|
|
106
|
+
const hookManager = await HookManager.new({
|
|
107
|
+
action: actionType,
|
|
108
|
+
hooksFile: validConfig.hooks,
|
|
124
109
|
logger: new Logger(instance.debugOptions),
|
|
125
|
-
timeout:
|
|
110
|
+
timeout: validConfig.hooksTimeout,
|
|
126
111
|
})
|
|
127
112
|
|
|
128
|
-
if(
|
|
129
|
-
|
|
113
|
+
if(hookManager)
|
|
114
|
+
instance.actions[actionType].hookManager = hookManager
|
|
130
115
|
}
|
|
131
116
|
}
|
|
132
117
|
|
|
@@ -135,6 +120,7 @@ export default class Core {
|
|
|
135
120
|
|
|
136
121
|
async processFiles(glob, startTime = process.hrtime()) {
|
|
137
122
|
const debug = this.logger.newDebug()
|
|
123
|
+
|
|
138
124
|
debug("Starting file processing with conveyor", 1)
|
|
139
125
|
|
|
140
126
|
const {output} = this.options
|
|
@@ -145,8 +131,8 @@ export default class Core {
|
|
|
145
131
|
|
|
146
132
|
// Instantiate the conveyor
|
|
147
133
|
const conveyor = new Conveyor(
|
|
148
|
-
this.
|
|
149
|
-
this.
|
|
134
|
+
this.actions.parse,
|
|
135
|
+
this.actions.print,
|
|
150
136
|
this.logger,
|
|
151
137
|
output,
|
|
152
138
|
)
|
|
@@ -171,12 +157,16 @@ export default class Core {
|
|
|
171
157
|
|
|
172
158
|
this.logger.debug(message, 1)
|
|
173
159
|
|
|
174
|
-
if(errored.
|
|
175
|
-
const failureRate = ((errored.length / totalFiles) * 100).toFixed(2)
|
|
176
|
-
const errorMessage =
|
|
177
|
-
|
|
160
|
+
if(errored.length > 0) {
|
|
161
|
+
// const failureRate = ((errored.length / totalFiles) * 100).toFixed(2)
|
|
162
|
+
// const errorMessage =
|
|
163
|
+
// `Errors processing ${errored.length} files [${failureRate}%]`
|
|
164
|
+
// const errorLines = errored.map(r => {
|
|
165
|
+
// const stackLine = log.lastStackLine(r.error, 0)
|
|
166
|
+
// return `\n- ${r.input.module}: ${stackLine} - ${r.error.message}`
|
|
167
|
+
// }).join("")
|
|
178
168
|
|
|
179
|
-
this.logger
|
|
169
|
+
// this.logger(errorMessage+errorLines)
|
|
180
170
|
}
|
|
181
171
|
|
|
182
172
|
debug("File processing complete", 1)
|
package/src/core/Discovery.js
CHANGED
|
@@ -5,30 +5,34 @@ import {execSync} from "child_process"
|
|
|
5
5
|
import * as FDUtil from "./util/FDUtil.js"
|
|
6
6
|
import * as ActionUtil from "./util/ActionUtil.js"
|
|
7
7
|
import * as DataUtil from "./util/DataUtil.js"
|
|
8
|
-
import * as ValidUtil from "./util/ValidUtil.js"
|
|
9
8
|
|
|
10
9
|
const {ls, resolveDirectory, resolveFilename, getFiles} = FDUtil
|
|
11
10
|
const {actionTypes, actionMetaRequirements, loadJson} = ActionUtil
|
|
12
11
|
const {isType} = DataUtil
|
|
13
|
-
const {assert} = ValidUtil
|
|
14
|
-
|
|
15
|
-
let debug
|
|
16
12
|
|
|
17
13
|
export default class Discovery {
|
|
18
14
|
#logger
|
|
15
|
+
#debug
|
|
19
16
|
|
|
20
17
|
constructor(core) {
|
|
21
18
|
this.core = core
|
|
22
19
|
this.#logger = core.logger
|
|
23
|
-
debug = this.#logger.newDebug()
|
|
20
|
+
this.#debug = this.#logger.newDebug()
|
|
24
21
|
}
|
|
25
22
|
|
|
26
23
|
/**
|
|
27
24
|
* Discover actions from local or global node_modules
|
|
28
25
|
*
|
|
26
|
+
* @param {object} [specific] Configuration options for action discovery
|
|
27
|
+
* @param {object} [specific.print] Print-related configuration options
|
|
28
|
+
* @param {object} [specific.parse] Parse-related configuration options
|
|
29
29
|
* @returns {Promise<object>} A map of discovered modules
|
|
30
30
|
*/
|
|
31
|
-
async discoverActions() {
|
|
31
|
+
async discoverActions(specific = {}) {
|
|
32
|
+
const debug = this.#debug
|
|
33
|
+
|
|
34
|
+
debug("Discovering actions", 2)
|
|
35
|
+
|
|
32
36
|
const bucket = []
|
|
33
37
|
const options = this.core.options ?? {}
|
|
34
38
|
|
|
@@ -42,35 +46,53 @@ export default class Discovery {
|
|
|
42
46
|
])),
|
|
43
47
|
)
|
|
44
48
|
} else {
|
|
45
|
-
debug("
|
|
49
|
+
debug("Mock path not set, discovering actions in node_modules", 1)
|
|
46
50
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
debug("Looking for actions in project's package.json", 2)
|
|
52
|
+
if(this.core.packageJson?.bedoc?.modules) {
|
|
53
|
+
const actions = this.core.packageJson?.bedoc?.modules
|
|
50
54
|
|
|
51
|
-
|
|
55
|
+
debug("Found %d actions in package.json: %d", 3, actions)
|
|
56
|
+
debug("Actions found in package.json action in package.json: %o", 3, actions)
|
|
52
57
|
|
|
53
|
-
|
|
54
|
-
|
|
58
|
+
if(actions && typeof(actions) === "object")
|
|
59
|
+
bucket.push(...actions)
|
|
60
|
+
else
|
|
61
|
+
debug("No actions found in package.json", 3)
|
|
62
|
+
} else {
|
|
63
|
+
debug("No actions found in project's package.json", 2)
|
|
55
64
|
}
|
|
56
65
|
|
|
66
|
+
debug("Looking for actions in node_modules (global and locally installed", 2)
|
|
57
67
|
const directories = [
|
|
58
|
-
// "c:/temp",
|
|
59
68
|
"./node_modules",
|
|
60
69
|
execSync("npm root -g").toString().trim(),
|
|
61
70
|
]
|
|
62
71
|
|
|
72
|
+
debug("Found %d directories to search for actions", 2, directories.length)
|
|
73
|
+
debug("Directories to search for actions: %o", 3, directories)
|
|
74
|
+
|
|
63
75
|
const moduleDirectories = directories.map(resolveDirectory)
|
|
64
76
|
for(const moduleDirectory of moduleDirectories) {
|
|
65
77
|
const {directories: dirs} = await ls(moduleDirectory.absolutePath)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
78
|
+
|
|
79
|
+
debug("Found %d directories in `%s`", 2,
|
|
80
|
+
dirs.length, moduleDirectory.absolutePath
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
const bedocDirs = dirs.filter(d => d.name.startsWith("bedoc-"))
|
|
84
|
+
debug("Found %d bedoc directories under %s", 2, bedocDirs.length, moduleDirectory.absolutePath)
|
|
85
|
+
|
|
86
|
+
const exports = bedocDirs.map(d => this.#getModuleExports(d))
|
|
87
|
+
debug("Found %d module exports under %s", 2, exports.length, moduleDirectory.absolutePath)
|
|
88
|
+
|
|
69
89
|
bucket.push(...exports.flat())
|
|
70
90
|
}
|
|
71
91
|
}
|
|
72
92
|
|
|
73
|
-
|
|
93
|
+
debug("Discovered %d actions", 2, bucket.length)
|
|
94
|
+
|
|
95
|
+
return await this.#loadActionsAndContracts(bucket, specific)
|
|
74
96
|
}
|
|
75
97
|
|
|
76
98
|
/**
|
|
@@ -80,11 +102,18 @@ export default class Discovery {
|
|
|
80
102
|
* @returns {object[]} The discovered module exports
|
|
81
103
|
*/
|
|
82
104
|
#getModuleExports(dirMap) {
|
|
105
|
+
const debug = this.#debug
|
|
106
|
+
debug("Getting module exports from `%s`", 3, dirMap.absolutePath)
|
|
107
|
+
|
|
83
108
|
const packageJsonFile = resolveFilename("package.json", dirMap)
|
|
109
|
+
debug("Loading package.json from `%s`", 3, packageJsonFile.absolutePath)
|
|
110
|
+
|
|
84
111
|
const packageJson = loadJson(packageJsonFile)
|
|
112
|
+
debug("Loaded package.json from `%s`", 3, packageJsonFile.absolutePath)
|
|
113
|
+
|
|
85
114
|
const bedocPackageJsonModules = packageJson.bedoc?.modules ?? []
|
|
86
|
-
const bedocModuleFiles = bedocPackageJsonModules.map(
|
|
87
|
-
resolveFilename(file, dirMap)
|
|
115
|
+
const bedocModuleFiles = bedocPackageJsonModules.map(file =>
|
|
116
|
+
resolveFilename(file, dirMap)
|
|
88
117
|
)
|
|
89
118
|
|
|
90
119
|
return bedocModuleFiles
|
|
@@ -95,68 +124,114 @@ export default class Discovery {
|
|
|
95
124
|
* respective contracts.
|
|
96
125
|
*
|
|
97
126
|
* @param {object[]} moduleFiles The module file objects to process
|
|
127
|
+
* @param {object} specific The specific actions to load
|
|
98
128
|
* @returns {Promise<object>} The discovered action
|
|
99
129
|
*/
|
|
100
|
-
async #loadActionsAndContracts(moduleFiles) {
|
|
130
|
+
async #loadActionsAndContracts(moduleFiles, specific) {
|
|
131
|
+
const debug = this.#debug
|
|
132
|
+
|
|
133
|
+
debug("Loading actions and contracts", 2)
|
|
134
|
+
debug("Loading %d module files", 2, moduleFiles.length)
|
|
135
|
+
debug("Specific actions to load: %o", 2, specific)
|
|
136
|
+
|
|
101
137
|
const resultActions = {}
|
|
138
|
+
actionTypes.forEach(actionType => (resultActions[actionType] = []))
|
|
139
|
+
|
|
140
|
+
// Tag the specific actions to load, so we can filter them later
|
|
141
|
+
for(const [type, file] of Object.entries(specific)) {
|
|
142
|
+
if(file) {
|
|
143
|
+
debug("Tagging specific action `%s` as `%s`", 3, file.absolutePath, type)
|
|
144
|
+
file.specificType = type
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const toLoad = [
|
|
149
|
+
...moduleFiles,
|
|
150
|
+
...Object.values(specific).filter(Boolean),
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
debug("Loading %d combined actions", 2, toLoad.length)
|
|
154
|
+
debug("Actions to load: %o", 3, toLoad)
|
|
155
|
+
|
|
156
|
+
const loadedActions = []
|
|
157
|
+
for(const file of toLoad) {
|
|
158
|
+
debug("Loading module `%s`", 2, file.absolutePath)
|
|
102
159
|
|
|
103
|
-
|
|
160
|
+
const loading = await this.#loadModule(file)
|
|
161
|
+
const loaded = loading.actions.map((action, index) => {
|
|
162
|
+
const contract = yaml.parse(loading.contracts[index])
|
|
104
163
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
164
|
+
return {file, action, contract}
|
|
165
|
+
})
|
|
166
|
+
loadedActions.push(...loaded)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
debug("Loaded %d actions", 2, loadedActions.length)
|
|
108
170
|
|
|
109
|
-
|
|
110
|
-
|
|
171
|
+
const filtered = []
|
|
172
|
+
for(const actionType of actionTypes) {
|
|
173
|
+
const file = specific[actionType]
|
|
174
|
+
const matchingActions = []
|
|
175
|
+
if(file) {
|
|
176
|
+
debug("Filtering actions for specific `%s`", 2, actionType)
|
|
177
|
+
const found = loadedActions.find(
|
|
178
|
+
e => e.file.absolutePath === file.absolutePath
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
if(!found)
|
|
182
|
+
throw new Error(`Could not find specific action: ${file.absolutePath}`)
|
|
183
|
+
|
|
184
|
+
matchingActions.push(found)
|
|
185
|
+
} else {
|
|
186
|
+
debug("No specific action required for `%s`", 2, actionType)
|
|
187
|
+
|
|
188
|
+
const found = loadedActions.filter(
|
|
189
|
+
e => e.action.meta.action === actionType
|
|
190
|
+
)
|
|
191
|
+
matchingActions.push(...found)
|
|
192
|
+
}
|
|
111
193
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
"Actions and contracts must be the same length",
|
|
115
|
-
1,
|
|
194
|
+
debug("Filtered %d actions for `%s`", 2,
|
|
195
|
+
matchingActions.length, actionType
|
|
116
196
|
)
|
|
117
197
|
|
|
118
|
-
|
|
198
|
+
filtered.push(...matchingActions)
|
|
199
|
+
}
|
|
119
200
|
|
|
120
|
-
|
|
121
|
-
const tempContract = contracts[i]
|
|
201
|
+
debug("Filtered %d actions", 2, filtered.length)
|
|
122
202
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
203
|
+
// Now check the metas for validity
|
|
204
|
+
for(const e of filtered) {
|
|
205
|
+
const {action, contract, file: moduleFile} = e
|
|
206
|
+
const meta = action.meta
|
|
207
|
+
if(!meta)
|
|
208
|
+
throw new TypeError("Action has no meta object:\n" +
|
|
209
|
+
JSON.stringify(moduleFile, null, 2) + "\n" +
|
|
210
|
+
JSON.stringify(action, null, 2))
|
|
129
211
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
212
|
+
const metaAction = meta.action
|
|
213
|
+
if(!metaAction)
|
|
214
|
+
throw new TypeError("Action has no meta action:\n" +
|
|
215
|
+
JSON.stringify(moduleFile, null, 2) + "\n" +
|
|
216
|
+
JSON.stringify(action, null, 2))
|
|
135
217
|
|
|
136
|
-
|
|
137
|
-
const metaAction = meta?.action
|
|
218
|
+
debug("Checking action `%s`", 2, metaAction)
|
|
138
219
|
|
|
139
|
-
|
|
220
|
+
const isValid = this.#validMeta(metaAction, {action, contract})
|
|
140
221
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
222
|
+
debug("Action `%o` in `%s` is %s", 3,
|
|
223
|
+
metaAction, moduleFile.module, isValid ? "valid" : "invalid"
|
|
224
|
+
)
|
|
144
225
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
result.accepted++
|
|
148
|
-
resultActions[actionType].push(curr)
|
|
149
|
-
continue
|
|
150
|
-
} else {
|
|
151
|
-
debug("Action is not a valid `%s` action", 3, actionType)
|
|
152
|
-
}
|
|
153
|
-
}
|
|
226
|
+
if(isValid) {
|
|
227
|
+
debug("Action is a valid `%s` action", 3, metaAction)
|
|
154
228
|
|
|
155
|
-
|
|
156
|
-
|
|
229
|
+
resultActions[metaAction].push({file: moduleFile, action, contract})
|
|
230
|
+
} else {
|
|
231
|
+
debug("Action is not a valid `%s` action", 3, metaAction)
|
|
157
232
|
}
|
|
158
233
|
|
|
159
|
-
debug("Processed
|
|
234
|
+
debug("Processed action `%s`", 2, metaAction)
|
|
160
235
|
}
|
|
161
236
|
|
|
162
237
|
for(const actionType of actionTypes) {
|
|
@@ -168,13 +243,92 @@ export default class Discovery {
|
|
|
168
243
|
return acc + resultActions[curr].length
|
|
169
244
|
}, 0)
|
|
170
245
|
|
|
171
|
-
debug("Loaded %d action definitions from %d modules", 2,
|
|
246
|
+
debug("Loaded %d action definitions from %d modules", 2,
|
|
247
|
+
total, moduleFiles.length
|
|
248
|
+
)
|
|
172
249
|
|
|
173
250
|
return resultActions
|
|
174
251
|
}
|
|
175
252
|
|
|
176
|
-
|
|
253
|
+
satisfyCriteria(actions, validatedConfig) {
|
|
254
|
+
const debug = this.#debug
|
|
255
|
+
const satisfied = {parse: [], print: []}
|
|
256
|
+
const toMatch = {
|
|
257
|
+
parse: {criterion: "language", config: "parser"},
|
|
258
|
+
print: {criterion: "format", config: "printer"}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
debug("Satisfying criteria for actions", 2)
|
|
262
|
+
for(const [actionType, search] of Object.entries(toMatch)) {
|
|
263
|
+
debug("Satisfying criteria for `%s` actions", 2, actionType)
|
|
264
|
+
|
|
265
|
+
const {criterion, config} = search
|
|
266
|
+
debug("Criterion: %s, Config: %s", 3, criterion, config)
|
|
267
|
+
|
|
268
|
+
// First let's check if we wanted something specific
|
|
269
|
+
if(validatedConfig[config]) {
|
|
270
|
+
debug("Checking for specific `%s` action", 3, actionType)
|
|
271
|
+
const found = actions[actionType].find(
|
|
272
|
+
a => a.file.specificType === actionType
|
|
273
|
+
)
|
|
274
|
+
if(found) {
|
|
275
|
+
debug("Found specific `%s` action", 3, actionType)
|
|
276
|
+
satisfied[actionType].push(found)
|
|
277
|
+
continue
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
debug("No specific `%s` action found", 3, actionType)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Hmm! We didn't find anything specific. Let's check the criterion
|
|
284
|
+
debug("Checking for `%s` actions with criterion `%s`", 3, actionType, criterion)
|
|
285
|
+
const found = actions[actionType].filter(a => {
|
|
286
|
+
debug("Meta criterion value: %o", 4, a.action.meta[criterion])
|
|
287
|
+
debug("Config criterion value: %o", 4, validatedConfig[criterion])
|
|
288
|
+
return a.action.meta[criterion] === validatedConfig[criterion]
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
debug("Found %d `%s` actions with criterion `%s`", 3,
|
|
292
|
+
found.length, actionType, criterion
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
// Shove them into the result!
|
|
296
|
+
satisfied[actionType].push(...found)
|
|
297
|
+
|
|
298
|
+
// That should about cover it!
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return satisfied
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Load a module and return its exports
|
|
306
|
+
*
|
|
307
|
+
* @param {object} module The module object to load
|
|
308
|
+
* @returns {Promise<object>} The module exports {actions, contracts}
|
|
309
|
+
*/
|
|
310
|
+
async #loadModule(module) {
|
|
311
|
+
const debug = this.#debug
|
|
312
|
+
|
|
313
|
+
debug("Loading module `%j`", 2, module)
|
|
314
|
+
|
|
315
|
+
const {absoluteUri} = module
|
|
316
|
+
const moduleExports = await import(absoluteUri)
|
|
317
|
+
|
|
318
|
+
return {file: module, ...moduleExports}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Validates the meta requirements for an action
|
|
323
|
+
*
|
|
324
|
+
* @param {string} actionType The action type to validate
|
|
325
|
+
* @param {object} toValidate - The action object to validate
|
|
326
|
+
* @returns {boolean} Whether the action object meets the meta requirements
|
|
327
|
+
*/
|
|
328
|
+
#validMeta(actionType, toValidate) {
|
|
329
|
+
const debug = this.#debug
|
|
177
330
|
debug("Checking meta requirements for `%s`", 3, actionType)
|
|
331
|
+
|
|
178
332
|
const requirements = actionMetaRequirements[actionType]
|
|
179
333
|
if(!requirements)
|
|
180
334
|
throw new Error(
|
|
@@ -8,17 +8,17 @@ const {assert} = ValidUtil
|
|
|
8
8
|
const freeze = Object.freeze
|
|
9
9
|
|
|
10
10
|
const hookEvents = freeze(["start", "section_load", "enter", "exit", "end"])
|
|
11
|
-
const hookPoints = freeze(
|
|
11
|
+
export const hookPoints = freeze(
|
|
12
12
|
await allocateObject(
|
|
13
13
|
hookEvents.map((event) => event.toUpperCase()),
|
|
14
14
|
hookEvents,
|
|
15
15
|
),
|
|
16
16
|
)
|
|
17
17
|
|
|
18
|
-
class
|
|
18
|
+
export default class HookManager {
|
|
19
19
|
#hooksFile = null
|
|
20
20
|
#log = null
|
|
21
|
-
#hooks =
|
|
21
|
+
#hooks = null
|
|
22
22
|
#action = null
|
|
23
23
|
#timeout = 1
|
|
24
24
|
|
|
@@ -49,11 +49,19 @@ class HooksManager {
|
|
|
49
49
|
return this.#timeout
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
get setup() {
|
|
53
|
+
return this.hooks?.setup || null
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
get cleanup() {
|
|
57
|
+
return this.hooks?.cleanup || null
|
|
58
|
+
}
|
|
59
|
+
|
|
52
60
|
static async new(arg) {
|
|
53
|
-
const instance = new
|
|
61
|
+
const instance = new HookManager(arg)
|
|
54
62
|
const debug = instance.log.newDebug()
|
|
55
63
|
|
|
56
|
-
debug("Creating new
|
|
64
|
+
debug("Creating new HookManager instance with args: `%o`", 2, arg)
|
|
57
65
|
|
|
58
66
|
const hooksFile = instance.hooksFile
|
|
59
67
|
|
|
@@ -116,7 +124,7 @@ class HooksManager {
|
|
|
116
124
|
1,
|
|
117
125
|
)
|
|
118
126
|
|
|
119
|
-
const hookExecution = await hook.call(this, ...args)
|
|
127
|
+
const hookExecution = await hook.call(this.hooks, ...args)
|
|
120
128
|
const hookTimeout = this.parent.timeout
|
|
121
129
|
const expireAsync = () =>
|
|
122
130
|
timeoutPromise(
|
|
@@ -134,10 +142,3 @@ class HooksManager {
|
|
|
134
142
|
}
|
|
135
143
|
}
|
|
136
144
|
}
|
|
137
|
-
|
|
138
|
-
export {
|
|
139
|
-
// Class
|
|
140
|
-
HooksManager,
|
|
141
|
-
// Constants
|
|
142
|
-
hookPoints,
|
|
143
|
-
}
|