@plugjs/plug 0.0.14 → 0.0.16

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 (212) hide show
  1. package/dist/async.cjs +20 -19
  2. package/dist/async.cjs.map +1 -1
  3. package/dist/async.d.ts +9 -9
  4. package/dist/async.mjs +18 -17
  5. package/dist/async.mjs.map +1 -1
  6. package/dist/build.cjs +113 -110
  7. package/dist/build.cjs.map +2 -2
  8. package/dist/build.d.ts +14 -56
  9. package/dist/build.mjs +114 -111
  10. package/dist/build.mjs.map +2 -2
  11. package/dist/files.cjs +2 -16
  12. package/dist/files.cjs.map +1 -1
  13. package/dist/files.d.ts +3 -10
  14. package/dist/files.mjs +3 -17
  15. package/dist/files.mjs.map +1 -1
  16. package/dist/fork.cjs +28 -40
  17. package/dist/fork.cjs.map +1 -1
  18. package/dist/fork.d.ts +6 -27
  19. package/dist/fork.mjs +29 -40
  20. package/dist/fork.mjs.map +1 -1
  21. package/dist/helpers.cjs +27 -61
  22. package/dist/helpers.cjs.map +2 -2
  23. package/dist/helpers.d.ts +29 -31
  24. package/dist/helpers.mjs +27 -62
  25. package/dist/helpers.mjs.map +2 -2
  26. package/dist/index.cjs +15 -2
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.ts +9 -13
  29. package/dist/index.mjs +7 -2
  30. package/dist/index.mjs.map +1 -1
  31. package/dist/log/colors.cjs +3 -1
  32. package/dist/log/colors.cjs.map +1 -1
  33. package/dist/log/colors.d.ts +2 -2
  34. package/dist/log/colors.mjs +3 -1
  35. package/dist/log/colors.mjs.map +1 -1
  36. package/dist/log/emit.d.ts +1 -1
  37. package/dist/log/logger.cjs +4 -0
  38. package/dist/log/logger.cjs.map +1 -1
  39. package/dist/log/logger.d.ts +4 -1
  40. package/dist/log/logger.mjs +4 -0
  41. package/dist/log/logger.mjs.map +1 -1
  42. package/dist/log/options.d.ts +1 -1
  43. package/dist/log/report.cjs +2 -11
  44. package/dist/log/report.cjs.map +1 -1
  45. package/dist/log/report.d.ts +36 -4
  46. package/dist/log/report.mjs +1 -10
  47. package/dist/log/report.mjs.map +1 -1
  48. package/dist/log.cjs +1 -1
  49. package/dist/log.cjs.map +1 -1
  50. package/dist/log.d.ts +6 -6
  51. package/dist/log.mjs +2 -2
  52. package/dist/log.mjs.map +1 -1
  53. package/dist/pipe.cjs +69 -26
  54. package/dist/pipe.cjs.map +1 -1
  55. package/dist/pipe.d.ts +178 -119
  56. package/dist/pipe.mjs +71 -24
  57. package/dist/pipe.mjs.map +1 -1
  58. package/dist/plugs/copy.cjs +15 -25
  59. package/dist/plugs/copy.cjs.map +2 -2
  60. package/dist/plugs/copy.d.ts +14 -13
  61. package/dist/plugs/copy.mjs +15 -17
  62. package/dist/plugs/copy.mjs.map +2 -2
  63. package/dist/plugs/coverage/analysis.d.ts +2 -2
  64. package/dist/plugs/coverage/report.cjs +1 -1
  65. package/dist/plugs/coverage/report.cjs.map +1 -1
  66. package/dist/plugs/coverage/report.d.ts +3 -3
  67. package/dist/plugs/coverage/report.mjs +1 -1
  68. package/dist/plugs/coverage/report.mjs.map +1 -1
  69. package/dist/plugs/coverage.cjs +20 -26
  70. package/dist/plugs/coverage.cjs.map +1 -1
  71. package/dist/plugs/coverage.d.ts +33 -30
  72. package/dist/plugs/coverage.mjs +21 -19
  73. package/dist/plugs/coverage.mjs.map +1 -1
  74. package/dist/plugs/debug.cjs +12 -36
  75. package/dist/plugs/debug.cjs.map +1 -1
  76. package/dist/plugs/debug.d.ts +4 -11
  77. package/dist/plugs/debug.mjs +30 -21
  78. package/dist/plugs/debug.mjs.map +1 -1
  79. package/dist/plugs/esbuild/fix-extensions.mjs +1 -1
  80. package/dist/plugs/esbuild/fix-extensions.mjs.map +1 -1
  81. package/dist/plugs/esbuild.cjs +14 -25
  82. package/dist/plugs/esbuild.cjs.map +1 -1
  83. package/dist/plugs/esbuild.d.ts +11 -16
  84. package/dist/plugs/esbuild.mjs +14 -17
  85. package/dist/plugs/esbuild.mjs.map +1 -1
  86. package/dist/plugs/eslint/runner.cjs +5 -6
  87. package/dist/plugs/eslint/runner.cjs.map +1 -1
  88. package/dist/plugs/eslint/runner.d.ts +6 -20
  89. package/dist/plugs/eslint/runner.mjs +6 -7
  90. package/dist/plugs/eslint/runner.mjs.map +1 -1
  91. package/dist/{task.cjs → plugs/eslint.cjs} +7 -24
  92. package/dist/plugs/eslint.cjs.map +6 -0
  93. package/dist/plugs/eslint.d.ts +34 -0
  94. package/dist/plugs/eslint.mjs +5 -0
  95. package/dist/plugs/eslint.mjs.map +6 -0
  96. package/dist/plugs/exec.cjs +20 -24
  97. package/dist/plugs/exec.cjs.map +1 -1
  98. package/dist/plugs/exec.d.ts +53 -53
  99. package/dist/plugs/exec.mjs +20 -23
  100. package/dist/plugs/exec.mjs.map +1 -1
  101. package/dist/plugs/filter.cjs +9 -19
  102. package/dist/plugs/filter.cjs.map +1 -1
  103. package/dist/plugs/filter.d.ts +21 -15
  104. package/dist/plugs/filter.mjs +10 -12
  105. package/dist/plugs/filter.mjs.map +1 -1
  106. package/dist/plugs/mocha/reporter.cjs +12 -6
  107. package/dist/plugs/mocha/reporter.cjs.map +1 -1
  108. package/dist/plugs/mocha/reporter.d.ts +0 -2
  109. package/dist/plugs/mocha/reporter.mjs +11 -4
  110. package/dist/plugs/mocha/reporter.mjs.map +1 -1
  111. package/dist/plugs/mocha/runner.cjs +4 -5
  112. package/dist/plugs/mocha/runner.cjs.map +1 -1
  113. package/dist/plugs/mocha/runner.d.ts +5 -31
  114. package/dist/plugs/mocha/runner.mjs +5 -6
  115. package/dist/plugs/mocha/runner.mjs.map +1 -1
  116. package/dist/plugs/mocha.cjs +22 -0
  117. package/dist/plugs/mocha.cjs.map +6 -0
  118. package/dist/plugs/mocha.d.ts +35 -0
  119. package/dist/plugs/mocha.mjs +5 -0
  120. package/dist/plugs/mocha.mjs.map +6 -0
  121. package/dist/plugs/rmf.cjs +4 -32
  122. package/dist/plugs/rmf.cjs.map +1 -1
  123. package/dist/plugs/rmf.d.ts +8 -12
  124. package/dist/plugs/rmf.mjs +25 -20
  125. package/dist/plugs/rmf.mjs.map +1 -1
  126. package/dist/plugs/tsc/compiler.d.ts +1 -1
  127. package/dist/plugs/tsc/options.d.ts +1 -1
  128. package/dist/plugs/tsc/report.d.ts +2 -2
  129. package/dist/plugs/tsc/runner.cjs +12 -11
  130. package/dist/plugs/tsc/runner.cjs.map +1 -1
  131. package/dist/plugs/tsc/runner.d.ts +4 -9
  132. package/dist/plugs/tsc/runner.mjs +12 -11
  133. package/dist/plugs/tsc/runner.mjs.map +1 -1
  134. package/dist/plugs/tsc.cjs +7 -0
  135. package/dist/plugs/tsc.cjs.map +6 -0
  136. package/dist/plugs/tsc.d.ts +36 -0
  137. package/dist/plugs/tsc.mjs +15 -0
  138. package/dist/plugs/tsc.mjs.map +6 -0
  139. package/dist/plugs.cjs +3 -5
  140. package/dist/plugs.cjs.map +1 -1
  141. package/dist/plugs.d.ts +10 -17
  142. package/dist/plugs.mjs +3 -5
  143. package/dist/plugs.mjs.map +1 -1
  144. package/dist/types.cjs +19 -0
  145. package/dist/types.cjs.map +6 -0
  146. package/dist/types.d.ts +71 -0
  147. package/dist/types.mjs +1 -0
  148. package/dist/types.mjs.map +6 -0
  149. package/dist/utils/caller.cjs +8 -11
  150. package/dist/utils/caller.cjs.map +2 -2
  151. package/dist/utils/caller.d.ts +2 -7
  152. package/dist/utils/caller.mjs +8 -11
  153. package/dist/utils/caller.mjs.map +2 -2
  154. package/dist/utils/options.cjs +4 -6
  155. package/dist/utils/options.cjs.map +1 -1
  156. package/dist/utils/options.d.ts +16 -15
  157. package/dist/utils/options.mjs +4 -6
  158. package/dist/utils/options.mjs.map +1 -1
  159. package/dist/utils/walk.d.ts +2 -2
  160. package/extra/cli.mjs +31 -20
  161. package/extra/ts-loader.mjs +6 -5
  162. package/package.json +8 -9
  163. package/src/async.ts +27 -19
  164. package/src/files.ts +6 -30
  165. package/src/fork.ts +35 -76
  166. package/src/helpers.ts +66 -99
  167. package/src/index.ts +10 -15
  168. package/src/log/colors.ts +4 -3
  169. package/src/log/emit.ts +4 -4
  170. package/src/log/logger.ts +12 -4
  171. package/src/log/options.ts +1 -1
  172. package/src/log/report.ts +8 -22
  173. package/src/log/spinner.ts +3 -3
  174. package/src/log.ts +9 -9
  175. package/src/paths.ts +1 -1
  176. package/src/pipe.ts +255 -170
  177. package/src/plugs/copy.ts +40 -31
  178. package/src/plugs/coverage/analysis.ts +4 -4
  179. package/src/plugs/coverage/report.ts +6 -5
  180. package/src/plugs/coverage.ts +64 -53
  181. package/src/plugs/debug.ts +28 -26
  182. package/src/plugs/esbuild/fix-extensions.ts +2 -2
  183. package/src/plugs/esbuild.ts +42 -46
  184. package/src/plugs/eslint/runner.ts +16 -31
  185. package/src/plugs/eslint.ts +42 -0
  186. package/src/plugs/exec.ts +93 -82
  187. package/src/plugs/filter.ts +42 -27
  188. package/src/plugs/mocha/reporter.ts +10 -5
  189. package/src/plugs/mocha/runner.ts +12 -38
  190. package/src/plugs/mocha.ts +41 -0
  191. package/src/plugs/rmf.ts +21 -25
  192. package/src/plugs/tsc/compiler.ts +1 -1
  193. package/src/plugs/tsc/options.ts +2 -2
  194. package/src/plugs/tsc/report.ts +2 -2
  195. package/src/plugs/tsc/runner.ts +24 -30
  196. package/src/plugs/tsc.ts +45 -0
  197. package/src/plugs.ts +10 -25
  198. package/src/types.ts +116 -0
  199. package/src/utils/caller.ts +11 -22
  200. package/src/utils/options.ts +49 -17
  201. package/src/utils/walk.ts +4 -4
  202. package/dist/run.cjs +0 -90
  203. package/dist/run.cjs.map +0 -6
  204. package/dist/run.d.ts +0 -89
  205. package/dist/run.mjs +0 -65
  206. package/dist/run.mjs.map +0 -6
  207. package/dist/task.cjs.map +0 -6
  208. package/dist/task.d.ts +0 -15
  209. package/dist/task.mjs +0 -14
  210. package/dist/task.mjs.map +0 -6
  211. package/src/run.ts +0 -159
  212. package/src/task.ts +0 -26
package/src/pipe.ts CHANGED
@@ -1,195 +1,244 @@
1
- import { assert } from './assert.js'
2
- import { Files } from './files.js'
3
- import { Run } from './run.js'
1
+ import {
2
+ AbsolutePath,
3
+ commonPath,
4
+ getAbsoluteParent,
5
+ getCurrentWorkingDirectory,
6
+ resolveAbsolutePath,
7
+ } from './paths'
8
+
9
+ import { sep } from 'path'
10
+ import { assert } from './assert'
11
+ import { requireContext } from './async'
12
+ import { Files } from './files'
13
+ import { ForkingPlug } from './fork'
14
+ import { getLogger, Logger } from './log'
15
+
16
+ /* ========================================================================== *
17
+ * PLUGS *
18
+ * ========================================================================== */
19
+
20
+ /** A convenience type indicating what can be returned by a {@link Plug}. */
21
+ export type PlugResult = Files | undefined | void
4
22
 
5
23
  /**
6
- * The {@link Plug} interface describes an extension mechanism for our build.
24
+ * The {@link Plug} interface describes _build plugin_.
25
+ *
26
+ * A {@link Plug} receives a {@link Files} instance in its input (for example
27
+ * a list of _source `.ts` files_) and optionally produces a possibly different
28
+ * list (for example the _compiled `.js` files_).
7
29
  */
8
- export interface Plug<T extends Files | undefined> {
9
- pipe(files: Files, run: Run): T | Promise<T>
30
+ export interface Plug<T extends PlugResult> {
31
+ pipe(files: Files, context: Context): T | Promise<T>
10
32
  }
11
33
 
12
- /**
13
- * A type identifying a {@link Plug} as a `function`
14
- */
15
- export type PlugFunction<T extends Files | undefined> = Plug<T>['pipe']
34
+ /** A type identifying a {@link Plug} as a `function` */
35
+ export type PlugFunction<T extends PlugResult> = Plug<T>['pipe']
36
+
37
+ /* ========================================================================== *
38
+ * PLUG CONTEXT *
39
+ * ========================================================================== */
16
40
 
17
41
  /**
18
- * A {@link Pipe} represents a sequence of operations performed by
19
- * a series of {@link Plug | Plugs}.
42
+ * The {@link Context} class defines the context in which a {@link Plug}
43
+ * is invoked.
20
44
  */
21
- export interface Pipe {
22
- /* Left empty, for definitions of installed plugs as extensions */
45
+ export class Context {
46
+ /** The directory of the file where the task was defined (convenience). */
47
+ public readonly buildDir: AbsolutePath
48
+ /** The {@link Logger} associated with this instance. */
49
+ public readonly log: Logger
50
+
51
+ constructor(
52
+ /** The absolute file name where the task was defined. */
53
+ public readonly buildFile: AbsolutePath,
54
+ /** The _name_ of the task associated with this {@link Context}. */
55
+ public readonly taskName: string,
56
+ ) {
57
+ this.buildDir = getAbsoluteParent(buildFile)
58
+ this.log = getLogger(taskName)
59
+ }
60
+
61
+ /**
62
+ * Resolve a (set of) path(s) in this {@link Context}.
63
+ *
64
+ * If the path (or first component thereof) starts with `@...`, then the
65
+ * resolved path will be relative to the directory containing the build file
66
+ * where the current task was defined, otherwise it will be relative to the
67
+ * current working directory.
68
+ */
69
+ resolve(path: string, ...paths: string[]): AbsolutePath {
70
+ // Paths starting with "@" are relative to the build file directory
71
+ if (path && path.startsWith('@')) {
72
+ // We can have paths like "@/../foo/bar" or "@../foo/bar"... both are ok
73
+ const components = path.substring(1).split(sep).filter((s) => !!s)
74
+ return resolveAbsolutePath(this.buildDir, ...components, ...paths)
75
+ }
76
+
77
+ // No path? Resolve to the CWD!
78
+ if (! path) return getCurrentWorkingDirectory()
79
+
80
+ // For all the rest, normal resolution!
81
+ return resolveAbsolutePath(getCurrentWorkingDirectory(), path, ...paths)
82
+ }
23
83
  }
24
84
 
85
+ /* ========================================================================== *
86
+ * PIPES *
87
+ * ========================================================================== */
88
+
25
89
  /**
26
- * The {@link Pipe} abstract class exposes the prototype upon which all
27
- * extension plugs will be installed on.
90
+ * A class that will be extended by {@link Pipe} where {@link install} will
91
+ * add prototype properties from installed {@link Plug}s
28
92
  */
29
- export abstract class Pipe implements Pipe {
30
- abstract plug(plug: Plug<Files> | PlugFunction<Files>): Pipe & Promise<Files>
31
- abstract plug(plug: Plug<undefined> | PlugFunction<undefined>): Promise<undefined>
93
+ abstract class PipeProto {
94
+ abstract plug(plug: Plug<PlugResult>): Pipe | Promise<undefined>
32
95
  }
33
96
 
34
-
35
- /** Implementation of our {@link Pipe}. */
36
- export class PipeImpl<T extends Files | undefined> extends Pipe implements Promise<T> {
37
- readonly #promise: Promise<T>
38
- readonly #run: Run
39
-
40
- constructor(start: T | Promise<T>, run: Run) {
97
+ /**
98
+ * The {@link Pipe} class defines processing pipeline where multiple
99
+ * {@link Plug}s can transform lists of {@link Files}.
100
+ */
101
+ export class Pipe extends PipeProto {
102
+ constructor(
103
+ private readonly _context: Context,
104
+ private readonly _run: () => Promise<Files>,
105
+ ) {
41
106
  super()
42
- this.#promise = Promise.resolve(start)
43
- this.#run = run
44
107
  }
45
108
 
46
- plug<T extends Files | undefined>(arg: Plug<T> | PlugFunction<T>): Pipe & Promise<T> {
109
+ plug(plug: Plug<Files>): Pipe
110
+ plug(plug: PlugFunction<Files>): Pipe
111
+ plug(plug: Plug<void | undefined>): Promise<undefined>
112
+ plug(plug: PlugFunction<void | undefined>): Promise<undefined>
113
+ plug(arg: Plug<PlugResult> | PlugFunction<PlugResult>): Pipe | Promise<undefined> {
47
114
  const plug = typeof arg === 'function' ? { pipe: arg } : arg
48
- const promise = this.#promise.then((files) => {
49
- assert(files, 'Pipe can not be further extended')
50
- return plug.pipe(files, this.#run)
115
+
116
+ const parent = this
117
+ const context = this._context
118
+ return new Pipe(context, async (): Promise<Files> => {
119
+ const previous = await parent.run()
120
+ const current = await plug.pipe(previous, context)
121
+ assert(current, 'Unable to extend pipe')
122
+ return current
51
123
  })
52
- return new PipeImpl(promise, this.#run)
53
124
  }
54
125
 
55
- then<T1 = T, T2 = never>(
56
- onfulfilled?: ((value: T) => T1 | PromiseLike<T1>) | null | undefined,
57
- onrejected?: ((reason: any) => T2 | PromiseLike<T2>) | null | undefined,
58
- ): Promise<T1 | T2> {
59
- return this.#promise.then(onfulfilled, onrejected)
126
+ run(): Promise<Files> {
127
+ return this._run()
60
128
  }
61
129
 
62
- catch<T0 = never>(
63
- onrejected?: ((reason: any) => T0 | PromiseLike<T0>) | null | undefined,
64
- ): Promise<T0 | T> {
65
- return this.#promise.catch(onrejected)
66
- }
130
+ /**
131
+ * Merge the results of several {@link Pipe}s into a single one.
132
+ *
133
+ * Merging is performed _in parallel_. When serial execution is to be desired,
134
+ * we can merge the awaited _result_ of the {@link Pipe}.
135
+ *
136
+ * For example:
137
+ *
138
+ * ```
139
+ * const pipe: Pipe = merge([
140
+ * // other tasks return `Pipe & Promise<Files>` so we can
141
+ * // direcrly await their result without invoking `run()`
142
+ * await this.anotherTask1(),
143
+ * await this.anotherTask2(),
144
+ * ])
145
+ * ```
146
+ */
147
+ static merge(pipes: (Pipe | Files | Promise<Files>)[]): Pipe {
148
+ const context = requireContext()
149
+ return new Pipe(context, async (): Promise<Files> => {
150
+ if (pipes.length === 0) return Files.builder(getCurrentWorkingDirectory()).build()
67
151
 
68
- finally(
69
- onfinally?: (() => void) | null | undefined,
70
- ): Promise<T> {
71
- return this.#promise.finally(onfinally)
72
- }
152
+ const [ first, ...other ] = await Promise.all(pipes.map((pipe) => {
153
+ return 'run' in pipe ? pipe.run() : pipe
154
+ }))
73
155
 
74
- [Symbol.toStringTag] = 'Pipe'
156
+ const firstDir = first.directory
157
+ const otherDirs = other.map((f) => f.directory)
158
+
159
+ const directory = commonPath(firstDir, ...otherDirs)
160
+
161
+ return Files.builder(directory).merge(first, ...other).build()
162
+ })
163
+ }
75
164
  }
76
165
 
77
166
  /* ========================================================================== *
78
- * PLUG INSTALLATION (INTERNAL) *
167
+ * PLUG INSTALLATION (NEW) *
79
168
  * ========================================================================== */
80
169
 
81
170
  /** The names which can be installed as direct plugs. */
82
- export type PlugName = string & Exclude<keyof Pipe, 'plug' | keyof Promise<Files>>
83
-
84
- /** A convenience type identifying a {@link Plug} constructor. */
85
- export type PlugConstructor = new (...args: any) => Plug<Files | undefined>
86
-
87
- /** Convert the resulting type of a {@link Plug} for use in a {@link Pipe} */
88
- type PlugReturnForPipe<T> =
89
- T extends Plug<infer R> ?
90
- R extends Files ?
91
- Promise<Files> & Pipe :
92
- R extends undefined ?
93
- Promise<undefined> :
94
- never :
95
- never
171
+ export type PlugName = string & Exclude<keyof Pipe, 'plug' | 'run'>
96
172
 
97
- /**
98
- * Map constructors into an array of all known overloads.
99
- *
100
- * This is a _royal_ pain in the ass, as we need to distinguish between
101
- * all possible number of overloads of a constructor... Limit to 5 of them!
102
- *
103
- * Also, the empty constructor (when specified in the overloads) will simply
104
- * match the first case (most overloads) and generate functions somewhat like
105
- *
106
- * (...args: unknown[]) => never
107
- * (...args: unknown[]) => never
108
- * (...args: unknown[]) => never
109
- * () => PlugReturnForPipe<R3>
110
- * (arg: Options) => PlugReturnForPipe<R4>
111
- *
112
- * Somehow inferring the result to `Function` or the right type and ANDing all
113
- * those together here doesn't work, so we create this array and we'll AND
114
- * all its members in the PipeExtension<...> type.
115
- */
116
- type PlugConstructorOverloads<T extends PlugConstructor> =
117
- T extends {
118
- new (...args: infer A0): infer R0
119
- new (...args: infer A1): infer R1
120
- new (...args: infer A2): infer R2
121
- new (...args: infer A3): infer R3
122
- new (...args: infer A4): infer R4
123
- } ? [
124
- R0 extends Plug<Files | undefined> ? ((...args: A0) => PlugReturnForPipe<R0>) : Function,
125
- R1 extends Plug<Files | undefined> ? ((...args: A1) => PlugReturnForPipe<R1>) : Function,
126
- R2 extends Plug<Files | undefined> ? ((...args: A2) => PlugReturnForPipe<R2>) : Function,
127
- R3 extends Plug<Files | undefined> ? ((...args: A3) => PlugReturnForPipe<R3>) : Function,
128
- R4 extends Plug<Files | undefined> ? ((...args: A4) => PlugReturnForPipe<R4>) : Function,
129
- ] :
130
- T extends {
131
- new (...args: infer A0): infer R0
132
- new (...args: infer A1): infer R1
133
- new (...args: infer A2): infer R2
134
- new (...args: infer A3): infer R3
135
- } ? [
136
- R0 extends Plug<Files | undefined> ? (...args: A0) => PlugReturnForPipe<R0> : Function,
137
- R1 extends Plug<Files | undefined> ? (...args: A1) => PlugReturnForPipe<R1> : Function,
138
- R2 extends Plug<Files | undefined> ? (...args: A2) => PlugReturnForPipe<R2> : Function,
139
- R3 extends Plug<Files | undefined> ? (...args: A3) => PlugReturnForPipe<R3> : Function,
140
- ] :
141
- T extends {
142
- new (...args: infer A0): infer R0
143
- new (...args: infer A1): infer R1
144
- new (...args: infer A2): infer R2
145
- } ? [
146
- R0 extends Plug<Files | undefined> ? (...args: A0) => PlugReturnForPipe<R0> : Function,
147
- R1 extends Plug<Files | undefined> ? (...args: A1) => PlugReturnForPipe<R1> : Function,
148
- R2 extends Plug<Files | undefined> ? (...args: A2) => PlugReturnForPipe<R2> : Function,
149
- ] :
150
- T extends {
151
- new (...args: infer A0): infer R0
152
- new (...args: infer A1): infer R1
153
- } ? [
154
- R0 extends Plug<Files | undefined> ? (...args: A0) => PlugReturnForPipe<R0> : Function,
155
- R1 extends Plug<Files | undefined> ? (...args: A1) => PlugReturnForPipe<R1> : Function,
156
- ] :
157
- T extends {
158
- new (...args: infer A0): infer R0
159
- } ? [
160
- R0 extends Plug<Files | undefined> ? (...args: A0) => PlugReturnForPipe<R0> : Function,
161
- ] :
162
- never
173
+ /** The parameters of the plug extension with the given name */
174
+ export type PipeParameters<Name extends PlugName> = PipeOverloads<Name>['args']
175
+
176
+ /** Extract arguments and return types from function overloads. */
177
+ type PipeOverloads<Name extends PlugName> =
178
+ Pipe[Name] extends {
179
+ (...args: infer A0): infer R0
180
+ (...args: infer A1): infer R1
181
+ (...args: infer A2): infer R2
182
+ (...args: infer A3): infer R3
183
+ (...args: infer A4): infer R4
184
+ } ?
185
+ | (R0 extends (Pipe | Promise<undefined>) ? { args: A0, ret: R0 } : never)
186
+ | (R1 extends (Pipe | Promise<undefined>) ? { args: A1, ret: R1 } : never)
187
+ | (R2 extends (Pipe | Promise<undefined>) ? { args: A2, ret: R2 } : never)
188
+ | (R3 extends (Pipe | Promise<undefined>) ? { args: A3, ret: R3 } : never)
189
+ | (R4 extends (Pipe | Promise<undefined>) ? { args: A4, ret: R4 } : never)
190
+ :
191
+ Pipe[Name] extends {
192
+ (...args: infer A0): infer R0
193
+ (...args: infer A1): infer R1
194
+ (...args: infer A2): infer R2
195
+ (...args: infer A3): infer R3
196
+ } ?
197
+ | (R0 extends (Pipe | Promise<undefined>) ? { args: A0, ret: R0 } : never)
198
+ | (R1 extends (Pipe | Promise<undefined>) ? { args: A1, ret: R1 } : never)
199
+ | (R2 extends (Pipe | Promise<undefined>) ? { args: A2, ret: R2 } : never)
200
+ | (R3 extends (Pipe | Promise<undefined>) ? { args: A3, ret: R3 } : never)
201
+ :
202
+ Pipe[Name] extends {
203
+ (...args: infer A0): infer R0
204
+ (...args: infer A1): infer R1
205
+ (...args: infer A2): infer R2
206
+ } ?
207
+ | (R0 extends (Pipe | Promise<undefined>) ? { args: A0, ret: R0 } : never)
208
+ | (R1 extends (Pipe | Promise<undefined>) ? { args: A1, ret: R1 } : never)
209
+ | (R2 extends (Pipe | Promise<undefined>) ? { args: A2, ret: R2 } : never)
210
+ :
211
+ Pipe[Name] extends {
212
+ (...args: infer A0): infer R0
213
+ (...args: infer A1): infer R1
214
+ } ?
215
+ | (R0 extends (Pipe | Promise<undefined>) ? { args: A0, ret: R0 } : never)
216
+ | (R1 extends (Pipe | Promise<undefined>) ? { args: A1, ret: R1 } : never)
217
+ :
218
+ Pipe[Name] extends {
219
+ (...args: infer A0): infer R0
220
+ } ?
221
+ | (R0 extends (Pipe | Promise<undefined>) ? { args: A0, ret: R0 } : never)
222
+ : never
223
+
224
+ /** The parameters of the plug extension with the given name */
225
+ type PipeResult<Name extends PlugName> = PipeOverloads<Name>['ret']
163
226
 
164
227
  /**
165
- * A convenience type to easily annotate installed {@link Plug Plugs}.
166
- *
167
- * See also {@link install}.
168
- *
169
- * ```
170
- * export class Write implements Plug {
171
- * // ... the plug implementation lives here
172
- * }
173
- *
174
- * install('write', Write)
175
- *
176
- * declare module '../pipe' {
177
- * export interface Pipe {
178
- * write: PipeExtension<typeof Write>
179
- * }
180
- * }
181
- * ```
228
+ * A type defining the correct constructor for a {@link Plug}, inferring
229
+ * argument types and instance type from the definitions in {@link Pipe}.
182
230
  */
183
- export type PipeExtension<T extends PlugConstructor, A = PlugConstructorOverloads<T>> =
184
- A extends readonly [ infer First, ...infer Rest ] ?
185
- First & PipeExtension<T, Rest> :
186
- A extends readonly [ infer Only ] ?
187
- Only :
188
- Function
231
+ type PlugConstructor<Name extends PlugName> =
232
+ PipeResult<Name> extends Pipe ?
233
+ new (...args: PipeParameters<Name>) => Plug<Files> :
234
+ PipeResult<Name> extends Promise<undefined> ?
235
+ new (...args: PipeParameters<Name>) => Plug<void | undefined> :
236
+ PipeResult<Name> extends (Pipe | Promise<undefined>) ?
237
+ new (...args: PipeParameters<Name>) => Plug<Files | void | undefined> :
238
+ never
189
239
 
190
240
  /**
191
- * Install a {@link Plug} into our {@link Pipe} prototype, and return a static
192
- * creator function for the {@link Plug} itself.
241
+ * Install a {@link Plug} into our {@link Pipe} prototype.
193
242
  *
194
243
  * This allows our shorthand syntax for well-defined plugs such as:
195
244
  *
@@ -202,30 +251,66 @@ export type PipeExtension<T extends PlugConstructor, A = PlugConstructorOverload
202
251
  * Use this alongside interface merging like:
203
252
  *
204
253
  * ```
205
- * export class Write implements Plug {
206
- * // ... the plug implementation lives here
207
- * }
208
- *
209
- * install('write', Write)
210
- *
211
- * declare module '../pipe' {
254
+ * declare module '@plugjs/plug/pipe' {
212
255
  * export interface Pipe {
213
- * write: PipeExtension<typeof Write>
256
+ * write(): Pipe
214
257
  * }
215
258
  * }
259
+ *
260
+ * install('write', class Write implements Plug {
261
+ * constructorg(...args: PipeParams<'write'>) {
262
+ * // here `args` is automatically inferred by whatever was declared above
263
+ * }
264
+ *
265
+ * // ... the plug implementation lives here
266
+ * })
216
267
  * ```
217
268
  */
218
- export function install<C extends PlugConstructor>(name: PlugName, ctor: C): void {
219
- /* This is quite hairy when it comes to types, so, just give up! :-P */
220
-
221
- function create(this: Pipe, ...args: any): Pipe & Promise<Files | undefined> {
269
+ export function install<
270
+ Name extends PlugName,
271
+ Ctor extends PlugConstructor<Name>,
272
+ >(name: Name, ctor: Ctor): void {
273
+ /* The function plugging the newly constructed plug in a pipe */
274
+ function plug(this: PipeProto, ...args: PipeParameters<Name>): Pipe | Promise<undefined> {
222
275
  // eslint-disable-next-line new-cap
223
- return this.plug(new ctor(...args) as any)
276
+ return this.plug(new ctor(...args))
224
277
  }
225
278
 
226
279
  /* Setup name so that stack traces look better */
227
- Object.defineProperty(create, 'name', { value: name })
280
+ Object.defineProperty(plug, 'name', { value: name })
228
281
 
229
282
  /* Inject the create function in the Pipe's prototype */
230
- Object.defineProperty(Pipe.prototype, name, { value: create })
283
+ Object.defineProperty(PipeProto.prototype, name, { value: plug })
284
+ }
285
+
286
+ /**
287
+ * Install a _forking_ {@link Plug} in the {@link Pipe}, in other words
288
+ * execute the plug in a separate process.
289
+ *
290
+ * As a contract, if the _last non-null_ parameter of the constructor is an
291
+ * object and contains the key `coverageDir`, the process will be forked with
292
+ * the approptiately resolved `NODE_V8_COVERAGE` environment variable.
293
+ *
294
+ * Also, forking plugs require some special attention:
295
+ *
296
+ * * plug functions are not supported, only classes implementing the
297
+ * {@link Plug} interface can be used with this.
298
+ *
299
+ * * the class itself _MUST_ be exported as the _default_ export for the
300
+ * `scriptFile` specified below. This is to simplify interoperability between
301
+ * CommonJS and ESM modules as we use dynamic `import(...)` statements.
302
+ */
303
+ export function installForking<Name extends PlugName>(
304
+ plugName: Name,
305
+ scriptFile: AbsolutePath,
306
+ ): void {
307
+ /** Extend out our ForkingPlug below */
308
+ const ctor = class extends ForkingPlug {
309
+ constructor(...args: any[]) {
310
+ super(scriptFile, args)
311
+ }
312
+ } as unknown as PlugConstructor<Name>
313
+
314
+ /** Install the plug in */
315
+ install(plugName, ctor)
231
316
  }
package/src/plugs/copy.ts CHANGED
@@ -1,10 +1,9 @@
1
- import { assert } from '../assert.js'
2
- import { Files } from '../files.js'
3
- import { $p } from '../log.js'
4
- import { assertAbsolutePath, getAbsoluteParent, resolveAbsolutePath } from '../paths.js'
5
- import { install, Plug } from '../pipe.js'
6
- import { Run } from '../run.js'
7
- import { chmod, copyFile, fsConstants, mkdir } from '../utils/asyncfs.js'
1
+ import { assert } from '../assert'
2
+ import { Files } from '../files'
3
+ import { $p } from '../log'
4
+ import { assertAbsolutePath, getAbsoluteParent, resolveAbsolutePath } from '../paths'
5
+ import { install, PipeParameters, Plug, Context } from '../pipe'
6
+ import { chmod, copyFile, fsConstants, mkdir } from '../utils/asyncfs'
8
7
 
9
8
  /** Options for copying files */
10
9
  export interface CopyOptions {
@@ -18,15 +17,37 @@ export interface CopyOptions {
18
17
  rename?: (relative: string) => string
19
18
  }
20
19
 
20
+ declare module '../pipe' {
21
+ export interface Pipe {
22
+ /**
23
+ * Copy the curent {@link Files} to a different directory
24
+ *
25
+ * @param directory The target directory where files will be copied to
26
+ */
27
+ copy(directory: string): Pipe
28
+ /**
29
+ * Copy the curent {@link Files} to a different directory
30
+ *
31
+ * @param directory The target directory where files will be copied to
32
+ * @param options Extra {@link CopyOptions | options} for the copy operation
33
+ */
34
+ copy(directory: string, options: CopyOptions): Pipe
35
+ }
36
+ }
37
+
38
+ /* ========================================================================== *
39
+ * INSTALLATION / IMPLEMENTATION *
40
+ * ========================================================================== */
41
+
21
42
  /** Copy the curent {@link Files} to a different directory */
22
- export class Copy implements Plug<Files> {
23
- constructor(directory: string, options?: CopyOptions)
43
+ install('copy', class Copy implements Plug<Files> {
44
+ constructor(...args: PipeParameters<'copy'>)
24
45
  constructor(
25
46
  private readonly _directory: string,
26
47
  private readonly _options: CopyOptions = {},
27
48
  ) {}
28
49
 
29
- async pipe(files: Files, run: Run): Promise<Files> {
50
+ async pipe(files: Files, context: Context): Promise<Files> {
30
51
  /* Destructure our options with some defaults and compute write flags */
31
52
  const { mode, dirMode, overwrite, rename = (s): string => s } = this._options
32
53
  const flags = overwrite ? fsConstants.COPYFILE_EXCL : 0
@@ -34,7 +55,8 @@ export class Copy implements Plug<Files> {
34
55
  const fmode = parseMode(mode)
35
56
 
36
57
  /* Our files builder for all written files */
37
- const builder = run.files(this._directory)
58
+ const directory = context.resolve(this._directory)
59
+ const builder = Files.builder(directory)
38
60
 
39
61
  /* Iterate through all the mappings of the source files */
40
62
  for (const [ relative, absolute ] of files.pathMappings()) {
@@ -44,7 +66,7 @@ export class Copy implements Plug<Files> {
44
66
 
45
67
  /* We never copy a file onto itself, but not fail either */
46
68
  if (target === absolute) {
47
- run.log.warn('Cowardly refusing to copy same file', $p(absolute))
69
+ context.log.warn('Cowardly refusing to copy same file', $p(absolute))
48
70
  continue
49
71
  }
50
72
 
@@ -56,44 +78,31 @@ export class Copy implements Plug<Files> {
56
78
  if (firstParent && (dmode !== undefined)) {
57
79
  assertAbsolutePath(firstParent)
58
80
  for (let dir = directory; ; dir = getAbsoluteParent(dir)) {
59
- run.log.trace(`Setting mode ${stringifyMode(dmode)} for directory`, $p(dir))
81
+ context.log.trace(`Setting mode ${stringifyMode(dmode)} for directory`, $p(dir))
60
82
  await chmod(dir, dmode)
61
83
  if (dir === firstParent) break
62
84
  }
63
85
  }
64
86
 
65
87
  /* Actually _copy_ the file */
66
- run.log.trace(`Copying "${$p(absolute)}" to "${$p(target)}"`)
88
+ context.log.trace(`Copying "${$p(absolute)}" to "${$p(target)}"`)
67
89
  await copyFile(absolute, target, flags)
68
90
 
69
91
  /* Set the mode, if we need to */
70
92
  if (fmode !== undefined) {
71
- run.log.trace(`Setting mode ${stringifyMode(fmode)} for file`, $p(target))
93
+ context.log.trace(`Setting mode ${stringifyMode(fmode)} for file`, $p(target))
72
94
  await chmod(target, fmode)
73
95
  }
74
96
 
75
97
  /* Record this file */
76
- builder.unchecked(relative)
98
+ builder.add(target)
77
99
  }
78
100
 
79
101
  const result = builder.build()
80
- run.log.info('Copied', result.length, 'files to', $p(builder.directory))
102
+ context.log.info('Copied', result.length, 'files to', $p(builder.directory))
81
103
  return result
82
104
  }
83
- }
84
-
85
- /* ========================================================================== *
86
- * INSTALLATION *
87
- * ========================================================================== */
88
-
89
- install('copy', Copy)
90
-
91
- declare module '../pipe.js' {
92
- export interface Pipe {
93
- /** Copy the curent {@link Files} to a different directory */
94
- copy: PipeExtension<typeof Copy>
95
- }
96
- }
105
+ })
97
106
 
98
107
  /* ========================================================================== *
99
108
  * INTERNALS *
@@ -1,9 +1,9 @@
1
1
  import { fileURLToPath, pathToFileURL } from 'node:url'
2
2
  import { RawSourceMap, SourceMapConsumer } from 'source-map'
3
- import { assert } from '../../assert.js'
4
- import { $gry, $p, Logger } from '../../log.js'
5
- import { AbsolutePath } from '../../paths.js'
6
- import { readFile } from '../../utils/asyncfs.js'
3
+ import { assert } from '../../assert'
4
+ import { $gry, $p, Logger } from '../../log'
5
+ import { AbsolutePath } from '../../paths'
6
+ import { readFile } from '../../utils/asyncfs'
7
7
 
8
8
  /* ========================================================================== *
9
9
  * V8 COVERAGE TYPES *
@@ -1,5 +1,4 @@
1
1
 
2
- import { parse } from '@babel/parser'
3
2
  import {
4
3
  Comment, isDeclaration,
5
4
  isExportDeclaration,
@@ -13,11 +12,13 @@ import {
13
12
  Node,
14
13
  VISITOR_KEYS
15
14
  } from '@babel/types'
15
+
16
+ import { parse } from '@babel/parser'
16
17
  import { pathToFileURL } from 'node:url'
17
- import { $p, Logger } from '../../log.js'
18
- import { AbsolutePath } from '../../paths.js'
19
- import { readFile } from '../../utils/asyncfs.js'
20
- import { CoverageAnalyser } from './analysis.js'
18
+ import { $p, Logger } from '../../log'
19
+ import { AbsolutePath } from '../../paths'
20
+ import { readFile } from '../../utils/asyncfs'
21
+ import { CoverageAnalyser } from './analysis'
21
22
 
22
23
  /* ========================================================================== *
23
24
  * EXPORTED CONSTANTS AND TYPES *