@plugjs/plug 0.0.18 → 0.0.20

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 (53) hide show
  1. package/dist/assert.cjs +3 -3
  2. package/dist/assert.cjs.map +1 -1
  3. package/dist/assert.d.ts +1 -1
  4. package/dist/assert.mjs +3 -3
  5. package/dist/assert.mjs.map +1 -1
  6. package/dist/build.cjs +2 -5
  7. package/dist/build.cjs.map +1 -1
  8. package/dist/build.mjs +4 -7
  9. package/dist/build.mjs.map +1 -1
  10. package/dist/failure.cjs +17 -6
  11. package/dist/failure.cjs.map +1 -1
  12. package/dist/failure.d.ts +5 -6
  13. package/dist/failure.mjs +17 -6
  14. package/dist/failure.mjs.map +1 -1
  15. package/dist/fork.cjs +9 -9
  16. package/dist/fork.cjs.map +1 -1
  17. package/dist/fork.mjs +9 -9
  18. package/dist/fork.mjs.map +1 -1
  19. package/dist/log/logger.cjs +4 -2
  20. package/dist/log/logger.cjs.map +1 -1
  21. package/dist/log/logger.mjs +4 -2
  22. package/dist/log/logger.mjs.map +1 -1
  23. package/dist/log/report.cjs +2 -2
  24. package/dist/log/report.cjs.map +1 -1
  25. package/dist/log/report.mjs +2 -2
  26. package/dist/log/report.mjs.map +1 -1
  27. package/dist/pipe.cjs +37 -15
  28. package/dist/pipe.cjs.map +1 -1
  29. package/dist/pipe.d.ts +21 -1
  30. package/dist/pipe.mjs +36 -14
  31. package/dist/pipe.mjs.map +1 -1
  32. package/dist/plugs/eslint/runner.cjs +1 -1
  33. package/dist/plugs/eslint/runner.cjs.map +1 -1
  34. package/dist/plugs/eslint/runner.mjs +1 -1
  35. package/dist/plugs/eslint/runner.mjs.map +1 -1
  36. package/dist/plugs/mocha/runner.cjs +1 -1
  37. package/dist/plugs/mocha/runner.cjs.map +1 -1
  38. package/dist/plugs/mocha/runner.mjs +1 -1
  39. package/dist/plugs/mocha/runner.mjs.map +1 -1
  40. package/dist/plugs/tsc/runner.cjs +4 -2
  41. package/dist/plugs/tsc/runner.cjs.map +1 -1
  42. package/dist/plugs/tsc/runner.mjs +5 -3
  43. package/dist/plugs/tsc/runner.mjs.map +1 -1
  44. package/package.json +2 -2
  45. package/src/assert.ts +3 -3
  46. package/src/failure.ts +28 -16
  47. package/src/fork.ts +9 -9
  48. package/src/log/logger.ts +11 -4
  49. package/src/log/report.ts +2 -2
  50. package/src/pipe.ts +51 -14
  51. package/src/plugs/eslint/runner.ts +1 -1
  52. package/src/plugs/mocha/runner.ts +1 -1
  53. package/src/plugs/tsc/runner.ts +5 -3
package/src/fork.ts CHANGED
@@ -89,7 +89,7 @@ export abstract class ForkingPlug implements Plug<PlugResult> {
89
89
 
90
90
  child.on('error', (error) => {
91
91
  context.log.error('Child process error', error)
92
- return done || reject(new BuildFailure({ logged: true }))
92
+ return done || reject(BuildFailure.fail())
93
93
  })
94
94
 
95
95
  child.on('message', (message: ForkResult) => {
@@ -100,16 +100,16 @@ export abstract class ForkingPlug implements Plug<PlugResult> {
100
100
  child.on('exit', (code, signal) => {
101
101
  if (signal) {
102
102
  context.log.error(`Child process exited with signal ${signal}`, $gry(`(pid=${child.pid})`))
103
- return done || reject(new BuildFailure({ logged: true }))
103
+ return done || reject(BuildFailure.fail())
104
104
  } else if (code !== 0) {
105
105
  context.log.error(`Child process exited with code ${code}`, $gry(`(pid=${child.pid})`))
106
- return done || reject(new BuildFailure({ logged: true }))
106
+ return done || reject(BuildFailure.fail())
107
107
  } else if (! result) {
108
108
  context.log.error('Child process exited with no result', $gry(`(pid=${child.pid})`))
109
- return done || reject(new BuildFailure({ logged: true }))
109
+ return done || reject(BuildFailure.fail())
110
110
  } else if (result.failed) {
111
111
  // definitely logged on the child side
112
- return done || reject(new BuildFailure({ logged: true }))
112
+ return done || reject(BuildFailure.fail())
113
113
  }
114
114
 
115
115
  /* We definitely have a successful result! */
@@ -123,16 +123,16 @@ export abstract class ForkingPlug implements Plug<PlugResult> {
123
123
  const result = child.send(message, (error) => {
124
124
  if (error) {
125
125
  context.log.error('Error sending message to child process (callback failure)', error)
126
- reject(new BuildFailure({ logged: true }))
126
+ reject(BuildFailure.fail())
127
127
  }
128
128
  })
129
129
  if (! result) {
130
130
  context.log.error('Error sending message to child process (send returned false)')
131
- reject(new BuildFailure({ logged: true }))
131
+ reject(BuildFailure.fail())
132
132
  }
133
133
  } catch (error) {
134
134
  context.log.error('Error sending message to child process (exception caught)', error)
135
- reject(new BuildFailure({ logged: true }))
135
+ reject(BuildFailure.fail())
136
136
  }
137
137
  }).finally(() => done = true)
138
138
  }
@@ -152,7 +152,7 @@ if ((process.argv[1] === requireFilename(__fileurl)) && (process.send)) {
152
152
  /* If we haven't processed our message in 5 seconds, fail _badly_ */
153
153
  const timeout = setTimeout(() => {
154
154
  // eslint-disable-next-line no-console
155
- console.error('Mocha not initialized in 5 seconds')
155
+ console.error('Fork not initialized in 5 seconds')
156
156
  process.exit(2)
157
157
  }, 5000)
158
158
 
package/src/log/logger.ts CHANGED
@@ -85,11 +85,18 @@ class LoggerImpl implements Logger {
85
85
  private _emit(level: LogLevel, args: [ any, ...any ]): this {
86
86
  if (this._level > level) return this
87
87
 
88
- // Filter out build failures that were already logged
88
+ // The `BuildFailure` is a bit special case
89
89
  const params = args.filter((arg) => {
90
90
  if (isBuildFailure(arg)) {
91
- if (arg.logged) return false // already logged? skip!
92
- return arg.logged = true // set logged to true and log!
91
+ // Filter out any previously logged build failure and mark
92
+ if (arg.logged) return false
93
+ arg.logged = true
94
+
95
+ // If the build failure has any root cause, log those
96
+ arg.errors.forEach((error) => this._emit(level, [ error ]))
97
+
98
+ // Log this only if it has a message
99
+ return !! arg.message
93
100
  } else {
94
101
  return true
95
102
  }
@@ -148,7 +155,7 @@ class LoggerImpl implements Logger {
148
155
 
149
156
  fail(...args: [ any, ...any ]): never {
150
157
  this._emit(ERROR, args)
151
- throw new BuildFailure({ logged: true })
158
+ throw BuildFailure.fail()
152
159
  }
153
160
 
154
161
  enter(): this
package/src/log/report.ts CHANGED
@@ -194,7 +194,7 @@ export class ReportImpl implements Report {
194
194
  if (! messages.length) {
195
195
  const options = { taskName: this._task, level: ERROR }
196
196
  this._emitter(options, [ 'No message for report record' ])
197
- throw new BuildFailure({ logged: true })
197
+ throw BuildFailure.fail()
198
198
  }
199
199
 
200
200
  const level = record.level
@@ -267,7 +267,7 @@ export class ReportImpl implements Report {
267
267
  done(showSources?: boolean | undefined): void {
268
268
  if (showSources == null) showSources = logOptions.showSources
269
269
  if (! this.empty) this._emit(showSources)
270
- if (this.errors) throw new BuildFailure({ logged: true })
270
+ if (this.errors) throw BuildFailure.fail()
271
271
  }
272
272
 
273
273
  private _emit(showSources: boolean): this {
package/src/pipe.ts CHANGED
@@ -87,22 +87,59 @@ export class Context {
87
87
  * ========================================================================== */
88
88
 
89
89
  /**
90
- * In pipe chains, we want to keep track of the _leaf_ `Files` promises (that
90
+ * In pipe chains, we want to keep track of the _leaf_ promises (that
91
91
  * is, when a derived pipe is created calling `plug` we want to track only the
92
92
  * new, derived, promise).
93
93
  *
94
94
  * We key these _leaf_ promises by _context_ (with a WeakMap), and those will
95
95
  * be awaited at the end of the task.
96
96
  */
97
- const contextPromises = new WeakMap<Context, Set<Promise<Result>>>()
97
+ const contextPromises = new WeakMap<Context, ContextPromises>()
98
98
 
99
- export function getContextPromises(context: Context): Set<Promise<Result>> {
100
- let promises = contextPromises.get(context)
101
- if (! promises) {
102
- promises = new Set<Promise<Files>>()
103
- contextPromises.set(context, promises)
99
+ /**
100
+ * An internal class recording _hot_ (failure will fail the task) and _cold_
101
+ * (failure will be ignored) {@link Promise}s for a task's {@link Context}.
102
+ */
103
+ export class ContextPromises {
104
+ private readonly _cold = new Set<Promise<Result>>()
105
+ private readonly _hot = new Set<Promise<Result>>()
106
+
107
+ /* Private constructor */
108
+ private constructor(readonly context: Context) {}
109
+
110
+ /** Track a {@link Promise} _hot_ (failure will fail the task) */
111
+ hot(promise: Promise<Result>): void {
112
+ this._cold.delete(promise)
113
+ this._hot.add(promise)
114
+ }
115
+
116
+ /** Track a {@link Promise} _cold_ (failure will be ignored) */
117
+ cold(promise: Promise<Result>): void {
118
+ this._hot.delete(promise)
119
+ this._cold.add(promise)
120
+ }
121
+
122
+ /**
123
+ * Await all tracked {@link Promise}s, triggering a build failure if any of
124
+ * the _hot_ ones is rejected.
125
+ */
126
+ static async wait(context: Context): Promise<void> {
127
+ const instance = contextPromises.get(context)
128
+ if (! instance) return
129
+
130
+ await Promise.allSettled([ ...instance._cold ])
131
+ await assertPromises([ ...instance._hot ])
132
+ }
133
+
134
+ /** Get a {@link ContextPromises} instance for the given {@link Context} */
135
+ static get(context: Context): ContextPromises {
136
+ let promises = contextPromises.get(context)
137
+ if (! promises) {
138
+ promises = new ContextPromises(context)
139
+ contextPromises.set(context, promises)
140
+ }
141
+ return promises
104
142
  }
105
- return promises
106
143
  }
107
144
 
108
145
  /**
@@ -127,7 +164,7 @@ export class Pipe extends PipeProto implements Promise<Files> {
127
164
  super()
128
165
 
129
166
  // New "Pipe", remember the promise!
130
- getContextPromises(_context).add(_promise)
167
+ ContextPromises.get(_context).hot(_promise)
131
168
  }
132
169
 
133
170
  /* ------------------------------------------------------------------------ *
@@ -152,7 +189,7 @@ export class Pipe extends PipeProto implements Promise<Files> {
152
189
  onrejected?: ((reason: any) => R2 | PromiseLike<R2>) | null | undefined,
153
190
  ): Promise<R1 | R2> {
154
191
  // We are delegating the handling of this promise to the caller
155
- getContextPromises(this._context).delete(this._promise)
192
+ ContextPromises.get(this._context).cold(this._promise)
156
193
  return this._promise.then(onfulfilled as (value: Result) => R1 | PromiseLike<R1>, onrejected)
157
194
  }
158
195
 
@@ -160,13 +197,13 @@ export class Pipe extends PipeProto implements Promise<Files> {
160
197
  onrejected?: ((reason: any) => R | PromiseLike<R>) | null | undefined,
161
198
  ): Promise<Files | R> {
162
199
  // We are delegating the handling of this promise to the caller
163
- getContextPromises(this._context).delete(this._promise)
200
+ ContextPromises.get(this._context).cold(this._promise)
164
201
  return this._promise.catch(onrejected) as Promise<Files | R>
165
202
  }
166
203
 
167
204
  finally(onfinally?: (() => void) | null | undefined): Promise<Files> {
168
205
  // We are delegating the handling of this promise to the caller
169
- getContextPromises(this._context).delete(this._promise)
206
+ ContextPromises.get(this._context).cold(this._promise)
170
207
  return this._promise.finally(onfinally) as Promise<Files>
171
208
  }
172
209
 
@@ -182,7 +219,7 @@ export class Pipe extends PipeProto implements Promise<Files> {
182
219
  const plug = typeof arg === 'function' ? { pipe: arg } : arg
183
220
 
184
221
  // We are creating a new "leaf" Pipe, we can forget our promise
185
- getContextPromises(this._context).delete(this._promise)
222
+ ContextPromises.get(this._context).cold(this._promise)
186
223
 
187
224
  // Create and return the new Pipe
188
225
  return new Pipe(this._context, this._promise.then(async (result) => {
@@ -214,7 +251,7 @@ export class Pipe extends PipeProto implements Promise<Files> {
214
251
  if (pipes.length === 0) return Files.builder(getCurrentWorkingDirectory()).build()
215
252
 
216
253
  // Await for all pipes / files / files promises
217
- const results = await assertPromises<Files>(pipes, 'Error in pipe while merging')
254
+ const results = await assertPromises<Files>(pipes)
218
255
 
219
256
  // Find the common directory between all the Files instances
220
257
  const [ firstDir, ...otherDirs ] = results.map((f) => f.directory)
@@ -60,7 +60,7 @@ export default class ESLint implements Plug<void> {
60
60
 
61
61
  /* In case of failures from promises, fail! */
62
62
  const { results, failures } = summary
63
- if (failures) throw new BuildFailure({ logged: true })
63
+ if (failures) throw BuildFailure.fail()
64
64
 
65
65
  /* Create our report */
66
66
  const report = context.log.report('ESLint Report')
@@ -40,7 +40,7 @@ export default class Mocha implements Plug<void> {
40
40
  return new Promise((resolve, reject) => {
41
41
  try {
42
42
  mocha.run((failures) => {
43
- if (failures) reject(new BuildFailure({ logged: true }))
43
+ if (failures) reject(BuildFailure.fail())
44
44
  resolve(undefined)
45
45
  })
46
46
  } catch (error) {
@@ -4,7 +4,7 @@ import { assertPromises } from '../../assert'
4
4
  import { BuildFailure } from '../../failure'
5
5
  import { Files } from '../../files'
6
6
  import { $p, log } from '../../log'
7
- import { AbsolutePath, resolveFile } from '../../paths'
7
+ import { AbsolutePath, resolveAbsolutePath, resolveFile } from '../../paths'
8
8
  import { Context, PipeParameters, Plug } from '../../pipe'
9
9
  import { parseOptions } from '../../utils/options'
10
10
  import { TypeScriptHost } from './compiler'
@@ -96,12 +96,14 @@ export default class Tsc implements Plug<Files> {
96
96
  promises.push(builder.write(fileName, code).then((file) => {
97
97
  log.trace('Written', $p(file))
98
98
  }).catch((error) => {
99
- throw new BuildFailure(`Error writing to ${fileName}`, [ error ])
99
+ const outFile = resolveAbsolutePath(outDir, fileName)
100
+ log.error('Error writing to', $p(outFile), error)
101
+ throw BuildFailure.fail()
100
102
  }))
101
103
  })
102
104
 
103
105
  /* Await for all files to be written and check */
104
- await assertPromises(promises, 'Error writing files')
106
+ await assertPromises(promises)
105
107
 
106
108
  /* Update report and fail on errors */
107
109
  updateReport(report, result.diagnostics, rootDir)