@plugjs/plug 0.0.1

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 (264) hide show
  1. package/LICENSE.md +211 -0
  2. package/NOTICE.md +13 -0
  3. package/README.md +7 -0
  4. package/dist/assert.cjs +72 -0
  5. package/dist/assert.cjs.map +6 -0
  6. package/dist/assert.mjs +41 -0
  7. package/dist/assert.mjs.map +6 -0
  8. package/dist/async.cjs +58 -0
  9. package/dist/async.cjs.map +6 -0
  10. package/dist/async.mjs +30 -0
  11. package/dist/async.mjs.map +6 -0
  12. package/dist/build.cjs +136 -0
  13. package/dist/build.cjs.map +6 -0
  14. package/dist/build.mjs +110 -0
  15. package/dist/build.mjs.map +6 -0
  16. package/dist/files.cjs +113 -0
  17. package/dist/files.cjs.map +6 -0
  18. package/dist/files.mjs +88 -0
  19. package/dist/files.mjs.map +6 -0
  20. package/dist/fork.cjs +177 -0
  21. package/dist/fork.cjs.map +6 -0
  22. package/dist/fork.mjs +151 -0
  23. package/dist/fork.mjs.map +6 -0
  24. package/dist/helpers.cjs +129 -0
  25. package/dist/helpers.cjs.map +6 -0
  26. package/dist/helpers.mjs +97 -0
  27. package/dist/helpers.mjs.map +6 -0
  28. package/dist/index.cjs +25 -0
  29. package/dist/index.cjs.map +6 -0
  30. package/dist/index.mjs +7 -0
  31. package/dist/index.mjs.map +6 -0
  32. package/dist/log/colors.cjs +129 -0
  33. package/dist/log/colors.cjs.map +6 -0
  34. package/dist/log/colors.mjs +93 -0
  35. package/dist/log/colors.mjs.map +6 -0
  36. package/dist/log/emit.cjs +109 -0
  37. package/dist/log/emit.cjs.map +6 -0
  38. package/dist/log/emit.mjs +83 -0
  39. package/dist/log/emit.mjs.map +6 -0
  40. package/dist/log/levels.cjs +75 -0
  41. package/dist/log/levels.cjs.map +6 -0
  42. package/dist/log/levels.mjs +41 -0
  43. package/dist/log/levels.mjs.map +6 -0
  44. package/dist/log/logger.cjs +129 -0
  45. package/dist/log/logger.cjs.map +6 -0
  46. package/dist/log/logger.mjs +104 -0
  47. package/dist/log/logger.mjs.map +6 -0
  48. package/dist/log/options.cjs +149 -0
  49. package/dist/log/options.cjs.map +6 -0
  50. package/dist/log/options.mjs +124 -0
  51. package/dist/log/options.mjs.map +6 -0
  52. package/dist/log/report.cjs +284 -0
  53. package/dist/log/report.cjs.map +6 -0
  54. package/dist/log/report.mjs +259 -0
  55. package/dist/log/report.mjs.map +6 -0
  56. package/dist/log/spinner.cjs +83 -0
  57. package/dist/log/spinner.cjs.map +6 -0
  58. package/dist/log/spinner.mjs +57 -0
  59. package/dist/log/spinner.mjs.map +6 -0
  60. package/dist/log.cjs +71 -0
  61. package/dist/log.cjs.map +6 -0
  62. package/dist/log.mjs +45 -0
  63. package/dist/log.mjs.map +6 -0
  64. package/dist/paths.cjs +158 -0
  65. package/dist/paths.cjs.map +6 -0
  66. package/dist/paths.mjs +122 -0
  67. package/dist/paths.mjs.map +6 -0
  68. package/dist/pipe.cjs +71 -0
  69. package/dist/pipe.cjs.map +6 -0
  70. package/dist/pipe.mjs +44 -0
  71. package/dist/pipe.mjs.map +6 -0
  72. package/dist/plugs/copy.cjs +95 -0
  73. package/dist/plugs/copy.cjs.map +6 -0
  74. package/dist/plugs/copy.mjs +64 -0
  75. package/dist/plugs/copy.mjs.map +6 -0
  76. package/dist/plugs/coverage/analysis.cjs +229 -0
  77. package/dist/plugs/coverage/analysis.cjs.map +6 -0
  78. package/dist/plugs/coverage/analysis.mjs +202 -0
  79. package/dist/plugs/coverage/analysis.mjs.map +6 -0
  80. package/dist/plugs/coverage/report.cjs +215 -0
  81. package/dist/plugs/coverage/report.cjs.map +6 -0
  82. package/dist/plugs/coverage/report.mjs +200 -0
  83. package/dist/plugs/coverage/report.mjs.map +6 -0
  84. package/dist/plugs/coverage.cjs +142 -0
  85. package/dist/plugs/coverage.cjs.map +6 -0
  86. package/dist/plugs/coverage.mjs +117 -0
  87. package/dist/plugs/coverage.mjs.map +6 -0
  88. package/dist/plugs/debug.cjs +50 -0
  89. package/dist/plugs/debug.cjs.map +6 -0
  90. package/dist/plugs/debug.mjs +25 -0
  91. package/dist/plugs/debug.mjs.map +6 -0
  92. package/dist/plugs/esbuild/bundle-locals.cjs +51 -0
  93. package/dist/plugs/esbuild/bundle-locals.cjs.map +6 -0
  94. package/dist/plugs/esbuild/bundle-locals.mjs +26 -0
  95. package/dist/plugs/esbuild/bundle-locals.mjs.map +6 -0
  96. package/dist/plugs/esbuild/check-dependencies.cjs +140 -0
  97. package/dist/plugs/esbuild/check-dependencies.cjs.map +6 -0
  98. package/dist/plugs/esbuild/check-dependencies.mjs +115 -0
  99. package/dist/plugs/esbuild/check-dependencies.mjs.map +6 -0
  100. package/dist/plugs/esbuild/fix-extensions.cjs +91 -0
  101. package/dist/plugs/esbuild/fix-extensions.cjs.map +6 -0
  102. package/dist/plugs/esbuild/fix-extensions.mjs +60 -0
  103. package/dist/plugs/esbuild/fix-extensions.mjs.map +6 -0
  104. package/dist/plugs/esbuild.cjs +109 -0
  105. package/dist/plugs/esbuild.cjs.map +6 -0
  106. package/dist/plugs/esbuild.mjs +83 -0
  107. package/dist/plugs/esbuild.mjs.map +6 -0
  108. package/dist/plugs/eslint/runner.cjs +91 -0
  109. package/dist/plugs/eslint/runner.cjs.map +6 -0
  110. package/dist/plugs/eslint/runner.mjs +68 -0
  111. package/dist/plugs/eslint/runner.mjs.map +6 -0
  112. package/dist/plugs/exec.cjs +128 -0
  113. package/dist/plugs/exec.cjs.map +6 -0
  114. package/dist/plugs/exec.mjs +96 -0
  115. package/dist/plugs/exec.mjs.map +6 -0
  116. package/dist/plugs/filter.cjs +59 -0
  117. package/dist/plugs/filter.cjs.map +6 -0
  118. package/dist/plugs/filter.mjs +34 -0
  119. package/dist/plugs/filter.mjs.map +6 -0
  120. package/dist/plugs/mocha/reporter.cjs +140 -0
  121. package/dist/plugs/mocha/reporter.cjs.map +6 -0
  122. package/dist/plugs/mocha/reporter.mjs +107 -0
  123. package/dist/plugs/mocha/reporter.mjs.map +6 -0
  124. package/dist/plugs/mocha/runner.cjs +73 -0
  125. package/dist/plugs/mocha/runner.cjs.map +6 -0
  126. package/dist/plugs/mocha/runner.mjs +44 -0
  127. package/dist/plugs/mocha/runner.mjs.map +6 -0
  128. package/dist/plugs/tsc/compiler.cjs +74 -0
  129. package/dist/plugs/tsc/compiler.cjs.map +6 -0
  130. package/dist/plugs/tsc/compiler.mjs +43 -0
  131. package/dist/plugs/tsc/compiler.mjs.map +6 -0
  132. package/dist/plugs/tsc/options.cjs +82 -0
  133. package/dist/plugs/tsc/options.cjs.map +6 -0
  134. package/dist/plugs/tsc/options.mjs +51 -0
  135. package/dist/plugs/tsc/options.mjs.map +6 -0
  136. package/dist/plugs/tsc/report.cjs +90 -0
  137. package/dist/plugs/tsc/report.cjs.map +6 -0
  138. package/dist/plugs/tsc/report.mjs +59 -0
  139. package/dist/plugs/tsc/report.mjs.map +6 -0
  140. package/dist/plugs/tsc/runner.cjs +101 -0
  141. package/dist/plugs/tsc/runner.cjs.map +6 -0
  142. package/dist/plugs/tsc/runner.mjs +72 -0
  143. package/dist/plugs/tsc/runner.mjs.map +6 -0
  144. package/dist/plugs.cjs +31 -0
  145. package/dist/plugs.cjs.map +6 -0
  146. package/dist/plugs.mjs +13 -0
  147. package/dist/plugs.mjs.map +6 -0
  148. package/dist/run.cjs +95 -0
  149. package/dist/run.cjs.map +6 -0
  150. package/dist/run.mjs +70 -0
  151. package/dist/run.mjs.map +6 -0
  152. package/dist/task.cjs +39 -0
  153. package/dist/task.cjs.map +6 -0
  154. package/dist/task.mjs +14 -0
  155. package/dist/task.mjs.map +6 -0
  156. package/dist/utils/asyncfs.cjs +143 -0
  157. package/dist/utils/asyncfs.cjs.map +6 -0
  158. package/dist/utils/asyncfs.mjs +83 -0
  159. package/dist/utils/asyncfs.mjs.map +6 -0
  160. package/dist/utils/caller.cjs +59 -0
  161. package/dist/utils/caller.cjs.map +6 -0
  162. package/dist/utils/caller.mjs +34 -0
  163. package/dist/utils/caller.mjs.map +6 -0
  164. package/dist/utils/match.cjs +69 -0
  165. package/dist/utils/match.cjs.map +6 -0
  166. package/dist/utils/match.mjs +38 -0
  167. package/dist/utils/match.mjs.map +6 -0
  168. package/dist/utils/options.cjs +41 -0
  169. package/dist/utils/options.cjs.map +6 -0
  170. package/dist/utils/options.mjs +16 -0
  171. package/dist/utils/options.mjs.map +6 -0
  172. package/dist/utils/walk.cjs +104 -0
  173. package/dist/utils/walk.cjs.map +6 -0
  174. package/dist/utils/walk.mjs +79 -0
  175. package/dist/utils/walk.mjs.map +6 -0
  176. package/extra/cli.mjs +212 -0
  177. package/extra/ts-loader.mjs +214 -0
  178. package/extra/webassembly.d.ts +11 -0
  179. package/package.json +57 -0
  180. package/src/assert.ts +47 -0
  181. package/src/async.ts +50 -0
  182. package/src/files.ts +129 -0
  183. package/src/fork.ts +263 -0
  184. package/src/helpers.ts +145 -0
  185. package/src/index.ts +20 -0
  186. package/src/log/colors.ts +119 -0
  187. package/src/log/emit.ts +125 -0
  188. package/src/log/levels.ts +65 -0
  189. package/src/log/logger.ts +171 -0
  190. package/src/log/options.ts +199 -0
  191. package/src/log/report.ts +433 -0
  192. package/src/log/spinner.ts +70 -0
  193. package/src/log.ts +68 -0
  194. package/src/paths.ts +213 -0
  195. package/src/pipe.ts +231 -0
  196. package/src/plugs/copy.ts +113 -0
  197. package/src/plugs/coverage/analysis.ts +395 -0
  198. package/src/plugs/coverage/report.ts +337 -0
  199. package/src/plugs/coverage.ts +194 -0
  200. package/src/plugs/debug.ts +35 -0
  201. package/src/plugs/esbuild/bundle-locals.ts +33 -0
  202. package/src/plugs/esbuild/check-dependencies.ts +158 -0
  203. package/src/plugs/esbuild/fix-extensions.ts +108 -0
  204. package/src/plugs/esbuild.ts +128 -0
  205. package/src/plugs/eslint/runner.ts +112 -0
  206. package/src/plugs/exec.ts +215 -0
  207. package/src/plugs/filter.ts +56 -0
  208. package/src/plugs/mocha/reporter.ts +152 -0
  209. package/src/plugs/mocha/runner.ts +77 -0
  210. package/src/plugs/tsc/compiler.ts +66 -0
  211. package/src/plugs/tsc/options.ts +97 -0
  212. package/src/plugs/tsc/report.ts +74 -0
  213. package/src/plugs/tsc/runner.ts +100 -0
  214. package/src/plugs.ts +33 -0
  215. package/src/run.ts +160 -0
  216. package/src/task.ts +26 -0
  217. package/src/utils/asyncfs.ts +82 -0
  218. package/src/utils/caller.ts +45 -0
  219. package/src/utils/match.ts +286 -0
  220. package/src/utils/options.ts +22 -0
  221. package/src/utils/walk.ts +136 -0
  222. package/types/assert.d.ts +18 -0
  223. package/types/async.d.ts +20 -0
  224. package/types/build.d.ts +56 -0
  225. package/types/files.d.ts +44 -0
  226. package/types/fork.d.ts +57 -0
  227. package/types/helpers.d.ts +49 -0
  228. package/types/index.d.ts +14 -0
  229. package/types/log/colors.d.ts +25 -0
  230. package/types/log/emit.d.ts +14 -0
  231. package/types/log/levels.d.ts +52 -0
  232. package/types/log/logger.d.ts +31 -0
  233. package/types/log/options.d.ts +40 -0
  234. package/types/log/report.d.ts +64 -0
  235. package/types/log/spinner.d.ts +2 -0
  236. package/types/log.d.ts +10 -0
  237. package/types/paths.d.ts +76 -0
  238. package/types/pipe.d.ts +152 -0
  239. package/types/plugs/copy.d.ts +27 -0
  240. package/types/plugs/coverage/analysis.d.ts +104 -0
  241. package/types/plugs/coverage/report.d.ts +53 -0
  242. package/types/plugs/coverage.d.ts +46 -0
  243. package/types/plugs/debug.d.ts +14 -0
  244. package/types/plugs/esbuild/bundle-locals.d.ts +6 -0
  245. package/types/plugs/esbuild/check-dependencies.d.ts +12 -0
  246. package/types/plugs/esbuild/fix-extensions.d.ts +29 -0
  247. package/types/plugs/esbuild.d.ts +24 -0
  248. package/types/plugs/eslint/runner.d.ts +22 -0
  249. package/types/plugs/exec.d.ts +90 -0
  250. package/types/plugs/filter.d.ts +23 -0
  251. package/types/plugs/mocha/reporter.d.ts +8 -0
  252. package/types/plugs/mocha/runner.d.ts +34 -0
  253. package/types/plugs/tsc/compiler.d.ts +24 -0
  254. package/types/plugs/tsc/options.d.ts +8 -0
  255. package/types/plugs/tsc/report.d.ts +5 -0
  256. package/types/plugs/tsc/runner.d.ts +13 -0
  257. package/types/plugs.d.ts +16 -0
  258. package/types/run.d.ts +89 -0
  259. package/types/task.d.ts +15 -0
  260. package/types/utils/asyncfs.d.ts +37 -0
  261. package/types/utils/caller.d.ts +7 -0
  262. package/types/utils/match.d.ts +216 -0
  263. package/types/utils/options.d.ts +15 -0
  264. package/types/utils/walk.d.ts +28 -0
@@ -0,0 +1,74 @@
1
+ import ts from 'typescript' // TypeScript does NOT support ESM modules
2
+
3
+ import { ERROR, NOTICE, Report, ReportLevel, ReportRecord, WARN } from '../../log.js'
4
+ import { AbsolutePath, resolveAbsolutePath } from '../../paths.js'
5
+
6
+ function convertMessageChain(chain: ts.DiagnosticMessageChain, indent = 0): string[] {
7
+ const message = `${''.padStart(indent * 2)}${chain.messageText}`
8
+
9
+ if (chain.next) {
10
+ const next = chain.next.map((c) => convertMessageChain(c, indent + 1))
11
+ return [ message, ...next.flat(1) ]
12
+ } else {
13
+ return [ message ]
14
+ }
15
+ }
16
+
17
+ function convertDiagnostics(
18
+ diagnostics: readonly ts.Diagnostic[],
19
+ directory: AbsolutePath,
20
+ ): ReportRecord[] {
21
+ return diagnostics.map((diagnostic): ReportRecord => {
22
+ // console.log(diagnostic)
23
+ void directory
24
+
25
+ // Convert the `DiagnosticCategory` to our level
26
+ let level: ReportLevel
27
+ switch (diagnostic.category) {
28
+ case ts.DiagnosticCategory.Error: level = ERROR; break
29
+ case ts.DiagnosticCategory.Warning: level = WARN; break
30
+ default: level = NOTICE
31
+ }
32
+
33
+ // Convert the `messageText` to a string
34
+ let message: string | string[]
35
+ if (typeof diagnostic.messageText === 'string') {
36
+ message = diagnostic.messageText
37
+ } else {
38
+ message = convertMessageChain(diagnostic.messageText)
39
+ }
40
+
41
+ // Simple variables
42
+ const tags = `TS${diagnostic.code}`
43
+
44
+
45
+ if (diagnostic.file) {
46
+ const { file: sourceFile, start, length } = diagnostic
47
+ const file = resolveAbsolutePath(directory, sourceFile.fileName)
48
+ const source = sourceFile.getFullText()
49
+
50
+ if (start !== undefined) {
51
+ const position = sourceFile.getLineAndCharacterOfPosition(start)
52
+ let { line, character: column } = position
53
+ column += 1
54
+ line += 1
55
+
56
+ return { level, message, tags, file, source, line, column, length }
57
+ } else {
58
+ return { level, message, tags, file, source }
59
+ }
60
+ } else {
61
+ return { level, message, tags }
62
+ }
63
+ })
64
+ }
65
+
66
+ /** Update a report, adding records from an array of {@link ts.Diagnostic} */
67
+ export function updateReport(
68
+ report: Report,
69
+ diagnostics: readonly ts.Diagnostic[],
70
+ directory: AbsolutePath,
71
+ ): void {
72
+ const records = convertDiagnostics(diagnostics, directory)
73
+ report.add(...records)
74
+ }
@@ -0,0 +1,100 @@
1
+ import ts from 'typescript' // TypeScript does NOT support ESM modules
2
+
3
+ import { failure } from '../../assert.js'
4
+ import { Files } from '../../files.js'
5
+ import { $p, log } from '../../log.js'
6
+ import { getCurrentWorkingDirectory, isFile } from '../../paths.js'
7
+ import { Plug } from '../../pipe.js'
8
+ import { Run } from '../../run.js'
9
+ import { parseOptions, ParseOptions } from '../../utils/options.js'
10
+ import { TypeScriptHost } from './compiler.js'
11
+ import { getCompilerOptions } from './options.js'
12
+ import { updateReport } from './report.js'
13
+
14
+ /* ========================================================================== *
15
+ * WORKER PLUG *
16
+ * ========================================================================== */
17
+
18
+ export default class Tsc implements Plug<Files> {
19
+ private readonly _tsconfig?: string
20
+ private readonly _options: ts.CompilerOptions
21
+
22
+ constructor()
23
+ constructor(config: string)
24
+ constructor(options: ts.CompilerOptions)
25
+ constructor(config: string, options: ts.CompilerOptions)
26
+
27
+ constructor(...args: ParseOptions<ts.CompilerOptions>) {
28
+ const { params: [ tsconfig ], options } = parseOptions(args, {})
29
+ this._tsconfig = tsconfig
30
+ this._options = options
31
+ }
32
+
33
+ async pipe(files: Files, run: Run): Promise<Files> {
34
+ const tsconfig = this._tsconfig ?
35
+ run.resolve(this._tsconfig) :
36
+ isFile(files.directory, 'tsconfig.json')
37
+
38
+ const overrides: ts.CompilerOptions = {
39
+ rootDir: files.directory, // by default, our "files" directory
40
+ ...this._options, // any other options specified in the constructor
41
+ }
42
+
43
+ const { errors, options } = await getCompilerOptions(
44
+ tsconfig, // resolved tsconfig.json from constructor, might be undefined
45
+ overrides, // overrides from constructor, might be an empty object
46
+ run.buildFile) // overrides are defined in the build file, sooo.....
47
+
48
+ const report = run.report('TypeScript Report')
49
+
50
+ // Update report and fail on errors
51
+ updateReport(report, errors, getCurrentWorkingDirectory())
52
+ if (report.errors) report.done(true)
53
+
54
+ const { rootDir, outDir } = options
55
+ const root = rootDir ? run.resolve(rootDir) : files.directory
56
+ const out = outDir ? run.resolve(outDir) : root
57
+
58
+ const host = new TypeScriptHost(root)
59
+
60
+ const paths = [ ...files.absolutePaths() ]
61
+ for (const path of paths) log.trace(`Compiling "${$p(path)}"`)
62
+
63
+ // Get our build file and create the master program
64
+ log.info('Compiling', paths.length, 'files')
65
+ log.debug('Compliation options', options)
66
+
67
+ const program = ts.createProgram(paths, options, host, undefined, errors)
68
+ const diagnostics = ts.getPreEmitDiagnostics(program)
69
+
70
+ // Update report and fail on errors
71
+ updateReport(report, diagnostics, root)
72
+ if (report.errors) report.done(true)
73
+
74
+ const builder = run.files(out)
75
+ const promises: Promise<void>[] = []
76
+ const result = program.emit(undefined, (fileName, code) => {
77
+ promises.push(builder.write(fileName, code).then((file) => {
78
+ log.trace('Written', $p(file))
79
+ }).catch((error) => {
80
+ run.log.error('Error writing to', fileName, error)
81
+ throw failure() // no more logs!
82
+ }))
83
+ })
84
+
85
+ // Update report and fail on errors
86
+ updateReport(report, result.diagnostics, root)
87
+ if (report.errors) report.done(true)
88
+
89
+ // Await for all files to be written and check
90
+ const settlements = await Promise.allSettled(promises)
91
+ const failures = settlements
92
+ .reduce((failures, s) => failures + s.status === 'rejected' ? 1 : 0, 0)
93
+ if (failures) throw failure() // already logged above
94
+
95
+ // All done, build our files and return it
96
+ const outputs = builder.build()
97
+ log.info('TSC produced', outputs.length, 'files into', $p(outputs.directory))
98
+ return outputs
99
+ }
100
+ }
package/src/plugs.ts ADDED
@@ -0,0 +1,33 @@
1
+ /* ========================================================================== *
2
+ * FORKING PLUGS *
3
+ * ========================================================================== */
4
+
5
+ import { installForking } from './fork.js'
6
+ import { requireResolve } from './paths.js'
7
+
8
+ import type ESLint from './plugs/eslint/runner.js'
9
+ import type Mocha from './plugs/mocha/runner.js'
10
+ import type Tsc from './plugs/tsc/runner.js'
11
+
12
+ declare module './pipe.js' {
13
+ export interface Pipe {
14
+ eslint: PipeExtension<typeof ESLint>
15
+ mocha: PipeExtension<typeof Mocha>
16
+ tsc: PipeExtension<typeof Tsc>
17
+ }
18
+ }
19
+
20
+ installForking('eslint', requireResolve(__fileurl, './plugs/eslint/runner'))
21
+ installForking('mocha', requireResolve(__fileurl, './plugs/mocha/runner'))
22
+ installForking('tsc', requireResolve(__fileurl, './plugs/tsc/runner'))
23
+
24
+ /* ========================================================================== *
25
+ * STANDARD IN-PROCESS PLUGS *
26
+ * ========================================================================== */
27
+
28
+ export * from './plugs/copy.js'
29
+ export * from './plugs/coverage.js'
30
+ export * from './plugs/debug.js'
31
+ export * from './plugs/esbuild.js'
32
+ export * from './plugs/exec.js'
33
+ export * from './plugs/filter.js'
package/src/run.ts ADDED
@@ -0,0 +1,160 @@
1
+ import { join } from 'node:path'
2
+ import { assert } from './assert.js'
3
+ import { Files, FilesBuilder } from './files.js'
4
+ import { createReport, getLevelNumber, getLogger, Logger, LogLevelString, Report } from './log.js'
5
+ import { AbsolutePath, getCurrentWorkingDirectory, isAbsolutePath, resolveAbsolutePath } from './paths.js'
6
+ import { Pipe, PipeImpl } from './pipe.js'
7
+ import { ParseOptions, parseOptions } from './utils/options.js'
8
+ import { walk, WalkOptions } from './utils/walk.js'
9
+
10
+ /** The {@link FindOptions} interface defines the options for finding files. */
11
+ export interface FindOptions extends WalkOptions {
12
+ /**
13
+ * The directory where to start looking for files according to the rules
14
+ * specified in {@link Run.resolve}.
15
+ */
16
+ directory?: string
17
+ }
18
+
19
+ /**
20
+ * The {@link Run} interface defines the context in which a {@link Task} is
21
+ * invoked.
22
+ *
23
+ * Runs keep track of the invocation stack (to avoid circular dependencies) and
24
+ * of the cached results for {@link Task} invocations.
25
+ */
26
+ export interface Run {
27
+ /**
28
+ * The _name_ of the task associated with this {@link Run} (if one is).
29
+ *
30
+ * Tasks can have different names in different builds, this refers to the
31
+ * _task name_ in the build being executed.
32
+ */
33
+ readonly taskName: string
34
+ /** The absolute file name of the build */
35
+ readonly buildFile: AbsolutePath,
36
+ /** For convenience, the directory of the build file */
37
+ readonly buildDir: AbsolutePath,
38
+ /** The {@link Logger} associated with this instance. */
39
+ readonly log: Logger
40
+
41
+ /** Set the logging level within this {@link Run} */
42
+ setLogLevel(level: LogLevelString): void
43
+
44
+ /** Call another {@link Task} from this one. */
45
+ call(name: string): Promise<Files | undefined>
46
+
47
+ /** Create a new {@link Report} with the given _title_ */
48
+ report(title: string): Report
49
+
50
+ /**
51
+ * Resolve a path in the context of this {@link Run}.
52
+ *
53
+ * If the path starts with `@...` it is considered to be relative to the
54
+ * {@link process.cwd | current working directory}, otherwise it will be
55
+ * resolved against the build file where the task was originally defined in.
56
+ */
57
+ resolve(...paths: string[]): AbsolutePath
58
+
59
+ /** Create a {@link FilesBuilder} cloning an existing {@link Files}. */
60
+ files(files: Files): FilesBuilder
61
+
62
+ /**
63
+ * Create a {@link FilesBuilder} instance resolving the directory specified
64
+ * according to the rules specified in {@link Run.resolve}.
65
+ */
66
+ files(...paths: string[]): FilesBuilder
67
+
68
+ /**
69
+ * Find files according to the globs and {@link FindOptions} specified.
70
+ */
71
+ find(glob: string, ...args: ParseOptions<FindOptions>): Pipe & Promise<Files>
72
+
73
+ /**
74
+ * Create a new {@link Pipe} wrapping the specified {@link Files}.
75
+ */
76
+ pipe(files: Files | Promise<Files>): Pipe & Promise<Files>
77
+ }
78
+
79
+ /** Constructor options for our default {@link Run} implementation */
80
+ export interface RunConstructionOptions {
81
+ readonly taskName: string,
82
+ readonly buildDir: AbsolutePath,
83
+ readonly buildFile: AbsolutePath,
84
+ readonly log?: Logger,
85
+ }
86
+
87
+ /** Our default {@link Run} implementation */
88
+ export class RunImpl implements Run {
89
+ readonly taskName: string
90
+ readonly buildFile: AbsolutePath
91
+ readonly buildDir: AbsolutePath
92
+ readonly log: Logger
93
+
94
+ constructor(options: RunConstructionOptions)
95
+
96
+ constructor({ taskName, buildDir, buildFile, log }: RunConstructionOptions) {
97
+ this.taskName = taskName
98
+ this.buildDir = buildDir
99
+ this.buildFile = buildFile
100
+ this.log = log || getLogger(taskName)
101
+ }
102
+
103
+ /** Set the logging level within this {@link Run} */
104
+ setLogLevel(level: LogLevelString): void {
105
+ this.log.level = getLevelNumber(level)
106
+ }
107
+
108
+ report(title: string): Report {
109
+ return createReport(title, this.taskName)
110
+ }
111
+
112
+ resolve(...paths: string[]): AbsolutePath {
113
+ const path = join(...paths)
114
+ if (! path) return this.buildDir
115
+
116
+ if (path.startsWith('@')) {
117
+ const relative = path.substring(1)
118
+ assert(! isAbsolutePath(relative), `Path component of "${path}" is absolute`)
119
+ return resolveAbsolutePath(getCurrentWorkingDirectory(), relative)
120
+ }
121
+
122
+ if (isAbsolutePath(path)) return path
123
+
124
+ return resolveAbsolutePath(this.buildDir, path)
125
+ }
126
+
127
+ files(files: Files): FilesBuilder
128
+ files(...paths: string[]): FilesBuilder
129
+ files(first: Files | string | undefined, ...paths: string[]): FilesBuilder {
130
+ if (typeof first === 'string') {
131
+ return Files.builder(this.resolve(first, ...paths))
132
+ } else if (first) {
133
+ return Files.builder(first)
134
+ } else {
135
+ return Files.builder(this.resolve())
136
+ }
137
+ }
138
+
139
+ find(glob: string, ...args: ParseOptions<FindOptions>): Pipe & Promise<Files> {
140
+ const { params, options: { directory, ...options } } = parseOptions(args, {})
141
+
142
+ const promise = Promise.resolve().then(async () => {
143
+ const builder = this.files(directory || '.')
144
+ for await (const file of walk(builder.directory, [ glob, ...params ], options)) {
145
+ builder.add(file)
146
+ }
147
+ return builder.build()
148
+ })
149
+
150
+ return this.pipe(promise)
151
+ }
152
+
153
+ pipe(files: Files | Promise<Files>): Pipe & Promise<Files> {
154
+ return new PipeImpl(files, this)
155
+ }
156
+
157
+ call(name: string): Promise<Files | undefined> {
158
+ throw new Error(`Unable to call task "${name}"`)
159
+ }
160
+ }
package/src/task.ts ADDED
@@ -0,0 +1,26 @@
1
+ import type { BuildContext, TaskDefinition, ThisBuild } from './build.js'
2
+ import type { Files } from './files.js'
3
+ import type { Run } from './run.js'
4
+
5
+ /* ========================================================================== *
6
+ * TASK *
7
+ * ========================================================================== */
8
+
9
+ export interface Task<T extends Files | undefined = Files | undefined> {
10
+ /** The {@link BuildContext} of where this task was originally defined */
11
+ readonly context: BuildContext
12
+
13
+ /** Invoked by the {@link Run} when actually executing this {@link Task} */
14
+ call(thisBuild: ThisBuild<any>, run: Run): Promise<T>
15
+ }
16
+
17
+ export class TaskImpl implements Task<Files | undefined> {
18
+ constructor(
19
+ readonly context: BuildContext,
20
+ private readonly _definition: TaskDefinition<any>,
21
+ ) {}
22
+
23
+ async call(self: ThisBuild<any>, run: Run): Promise<Files | undefined> {
24
+ return (await this._definition.call(self, self, run)) || undefined
25
+ }
26
+ }
@@ -0,0 +1,82 @@
1
+ import { constants } from 'node:fs'
2
+ import fsp from 'node:fs/promises'
3
+
4
+ /*
5
+ * I have no idea why sometimes stacks don't have a trace when coming out of
6
+ * the "node:fs/promises" api... There is a _stack_ property on the object
7
+ * but it simply includes the first line (if so), no thing further...
8
+ *
9
+ * Upon further inspection, it _seems_ this is related to some issues with the
10
+ * Error.captureStackTrace(...) function. If we pass a function to it as
11
+ * second parameter in our functions below, we end up with no stacks as well!
12
+ *
13
+ * So, for now, let's catch all errors, invoke `Error.captureStackTrace`
14
+ * manually without any parameter (besides the error itself), and fail with
15
+ * this.
16
+ *
17
+ * Hopefully, it'll get fixed, eventually, but for now let's keep this wrapper
18
+ * around!
19
+ */
20
+
21
+ /* Process every entry in "node:fs/promises" */
22
+ const fs = Object.entries(fsp as any).reduce((fs, [ key, val ]) => {
23
+ if (typeof val === 'function') {
24
+ /* If the value is a function, wrap it! */
25
+ const f = function(...args: any[]): any {
26
+ /* Call the function, and _catch_ any error */
27
+ return val.apply(fsp, args).catch((error: any) => {
28
+ /* For any error caught, we fill in the stack trace */
29
+ Error.captureStackTrace(error)
30
+ throw error
31
+ })
32
+ }
33
+
34
+ /* Make sure that the functions are called correctly */
35
+ Object.defineProperty(f, 'name', { value: key })
36
+ /* Assign the wrapper to our exports */
37
+ fs[key] = f
38
+ } else {
39
+ /* Not a function, no wrapping... */
40
+ fs[key] = val
41
+ }
42
+
43
+ /* Return the "reduced" exports */
44
+ return fs
45
+ }, { constants } as any) as typeof fsp & { constants: typeof constants }
46
+
47
+ /* Export _our_ version of the "node:fs/promises" module */
48
+ export default fs
49
+
50
+ /* Export all the wrappers to "node:fs/promises" individually */
51
+ export const access = fs.access
52
+ export const copyFile = fs.copyFile
53
+ export const cp = fs.cp
54
+ export const open = fs.open
55
+ export const opendir = fs.opendir
56
+ export const rename = fs.rename
57
+ export const truncate = fs.truncate
58
+ export const rm = fs.rm
59
+ export const rmdir = fs.rmdir
60
+ export const mkdir = fs.mkdir
61
+ export const readdir = fs.readdir
62
+ export const readlink = fs.readlink
63
+ export const symlink = fs.symlink
64
+ export const lstat = fs.lstat
65
+ export const stat = fs.stat
66
+ export const link = fs.link
67
+ export const unlink = fs.unlink
68
+ export const chmod = fs.chmod
69
+ export const lchmod = fs.lchmod
70
+ export const lchown = fs.lchown
71
+ export const chown = fs.chown
72
+ export const utimes = fs.utimes
73
+ export const lutimes = fs.lutimes
74
+ export const realpath = fs.realpath
75
+ export const mkdtemp = fs.mkdtemp
76
+ export const writeFile = fs.writeFile
77
+ export const appendFile = fs.appendFile
78
+ export const readFile = fs.readFile
79
+ export const watch = fs.watch
80
+
81
+ /* Export constants from "node:/fs" in addition */
82
+ export { constants } from 'node:fs'
@@ -0,0 +1,45 @@
1
+ import { statSync } from 'node:fs'
2
+ import { fileURLToPath } from 'node:url'
3
+ import { assert } from '../assert.js'
4
+ import { AbsolutePath, assertAbsolutePath } from '../paths.js'
5
+
6
+ export interface Location {
7
+ file: AbsolutePath,
8
+ line?: number | undefined,
9
+ column?: number | undefined,
10
+ }
11
+
12
+ export function findCaller(of: (...args: any[]) => any): Location {
13
+ const oldPrepareStackTrace = Error.prepareStackTrace
14
+
15
+ try {
16
+ Error.prepareStackTrace = (_, stackTraces): Location | undefined => {
17
+ const nullableFileOrUrl = stackTraces[0].getFileName()
18
+ if (! nullableFileOrUrl) return
19
+
20
+ const nullableLine = stackTraces[0].getColumnNumber()
21
+ const nullableColumn = stackTraces[0].getColumnNumber()
22
+
23
+ const line = typeof nullableLine === 'number' ? nullableLine : undefined
24
+ const column = typeof nullableColumn === 'number' ? nullableColumn : undefined
25
+
26
+ const file =
27
+ nullableFileOrUrl.startsWith('file:/') ?
28
+ fileURLToPath(nullableFileOrUrl) :
29
+ nullableFileOrUrl
30
+
31
+ assertAbsolutePath(file)
32
+ return { file, line, column }
33
+ }
34
+
35
+ const record: { stack?: Location } = {}
36
+ Error.captureStackTrace(record, of)
37
+ const location = record.stack
38
+
39
+ assert(location, 'Unable to determine build file name')
40
+ assert(statSync(location.file).isFile(), `Build file "${location.file}" not found`)
41
+ return location
42
+ } finally {
43
+ Error.prepareStackTrace = oldPrepareStackTrace
44
+ }
45
+ }