@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,158 @@
1
+ import { Message, OnStartResult, Plugin } from 'esbuild'
2
+ import { currentRun } from '../../async.js'
3
+ import { $p } from '../../log.js'
4
+ import { AbsolutePath } from '../../paths.js'
5
+ import { readFile } from '../../utils/asyncfs.js'
6
+ import { ParseOptions, parseOptions } from '../../utils/options.js'
7
+
8
+ export interface CheckDependenciesOptions {
9
+ allowDev?: boolean | 'warn' | 'error',
10
+ allowPeer?: boolean | 'warn' | 'error',
11
+ allowOptional?: boolean | 'warn' | 'error',
12
+ allowUnused?: boolean | 'warn' | 'error',
13
+ ignored?: string[]
14
+ }
15
+
16
+ export function checkDependencies(): Plugin
17
+ export function checkDependencies(packageJson: string): Plugin
18
+ export function checkDependencies(options: CheckDependenciesOptions): Plugin
19
+ export function checkDependencies(packageJson: string, options: CheckDependenciesOptions): Plugin
20
+
21
+ export function checkDependencies(...args: ParseOptions<CheckDependenciesOptions>): Plugin {
22
+ const run = currentRun() // outside of "onStart", goes into esbuild domain
23
+
24
+ const { params, options } = parseOptions(args, {
25
+ ignored: [] as string[],
26
+ allowDev: false,
27
+ allowPeer: true,
28
+ allowOptional: true,
29
+ allowUnused: false,
30
+ })
31
+
32
+ const allowDev = convertOption(options.allowDev)
33
+ const allowPeer = convertOption(options.allowPeer)
34
+ const allowOptional = convertOption(options.allowOptional)
35
+ const allowUnused = convertOption(options.allowUnused)
36
+
37
+ const dependencies: string[] = []
38
+ const devDependencies: string[] = []
39
+ const peerDependencies: string[] = []
40
+ const optionalDependencies: string[] = []
41
+ const ignored = new Set(options.ignored)
42
+ const used = new Set<string>()
43
+
44
+ return {
45
+ name: 'check-dependencies',
46
+ setup(build): void {
47
+ /* When using this, we fake esbuild's "bundle" functionality */
48
+ build.initialOptions.bundle = true
49
+
50
+ let packageJson: AbsolutePath
51
+
52
+ build.onStart(async (): Promise<OnStartResult | void> => {
53
+ if (! run) return { errors: [ { text: 'Unable to find current Run' } ] }
54
+
55
+ const resolved = run.resolve(params[0] || '@package.json')
56
+ packageJson = resolved
57
+
58
+ try {
59
+ const data = await readFile(resolved, 'utf-8')
60
+ const json = JSON.parse(data)
61
+ dependencies.push(...dependencyKeys(json.dependencies))
62
+ devDependencies.push(...dependencyKeys(json.devDependencies))
63
+ peerDependencies.push(...dependencyKeys(json.peerDependencies))
64
+ optionalDependencies.push(...dependencyKeys(json.optionalDependencies))
65
+ } catch (error) {
66
+ return { errors: [ { text: `Unable to parse ${$p(resolved)}` } ] }
67
+ }
68
+ })
69
+
70
+ /* Intercept resolution */
71
+ build.onResolve({ filter: /.*/ }, (args) => {
72
+ if (args.importer.match(/\/node_modules\//)) return // only our sources
73
+ if (args.path.startsWith('node:')) return // node imports
74
+ if (args.path.startsWith('.')) return // local imports
75
+
76
+ // Normal dependencies get the green light immediately
77
+ if (dependencies.includes(args.path)) {
78
+ used.add(args.path)
79
+ return { external: true }
80
+ }
81
+
82
+ // In order, here, we first check "optional" and "peers" (which should)
83
+ // also have a corresponding entry in "dev" for things to work, then
84
+ // "dev" is definitely checked last
85
+ const [ result, label ] =
86
+ optionalDependencies.includes(args.path) ? [ allowOptional, 'an optional' ] as const :
87
+ peerDependencies.includes(args.path) ? [ allowPeer, 'a peer' ] as const :
88
+ devDependencies.includes(args.path) ? [ allowDev, 'a dev' ] as const :
89
+ [ 'error', undefined ] as const
90
+
91
+ // If we're told to ignore, then... IGNORE!
92
+ if (ignored.has(args.path)) return { external: true }
93
+ if (result === 'ignore') return { external: true }
94
+
95
+ // Prep the message
96
+ const text = label ?
97
+ `Dependency "${args.path}" is ${label} dependency` :
98
+ `Dependency "${args.path}" not specified in "package.json"`
99
+
100
+ // Return the proper error or warning
101
+ return result === 'warn' ?
102
+ { external: true, warnings: [ { text } ] } :
103
+ { external: true, errors: [ { text } ] }
104
+ })
105
+
106
+ /* Check for unused */
107
+ build.onEnd((result) => {
108
+ if (allowUnused === 'ignore') return
109
+
110
+ // Figure out every unused dependency
111
+ const unused = new Set(dependencies)
112
+ ignored.forEach((dep) => unused.delete(dep))
113
+ used.forEach((dep) => unused.delete(dep))
114
+
115
+ // Convert the dependency name into a "message"
116
+ const messages = [ ...unused ]
117
+ .map((dep) => `Unused dependency "${dep}"`)
118
+ .map((text): Message => ({
119
+ id: '',
120
+ pluginName: 'check-dependencies',
121
+ location: {
122
+ file: packageJson,
123
+ namespace: 'file',
124
+ line: 0,
125
+ column: 0,
126
+ length: 0,
127
+ lineText: '',
128
+ suggestion: '',
129
+ },
130
+ text,
131
+ notes: [],
132
+ detail: undefined,
133
+ }))
134
+
135
+ // Inject our messages either as warnings or errors
136
+ if (allowUnused === 'warn') {
137
+ result.warnings.push(...messages)
138
+ } else {
139
+ result.errors.push(...messages)
140
+ }
141
+ })
142
+ },
143
+ }
144
+ }
145
+
146
+ function convertOption(option?: boolean | 'warn' | 'error'): 'ignore' | 'warn' | 'error' {
147
+ if (option === 'warn') return 'warn'
148
+ if (option === 'error') return 'error'
149
+ if (option) return 'ignore'
150
+ return 'error'
151
+ }
152
+
153
+
154
+ function dependencyKeys(dependencies: any): string[] {
155
+ if (! dependencies) return []
156
+ if (typeof dependencies !== 'object') return []
157
+ return Object.keys(dependencies).filter((key) => typeof key === 'string')
158
+ }
@@ -0,0 +1,108 @@
1
+ import path from 'node:path'
2
+
3
+ import { Plugin } from 'esbuild'
4
+ import { assertAbsolutePath, isFile, resolveAbsolutePath } from '../../paths.js'
5
+ import { stat } from '../../utils/asyncfs.js'
6
+
7
+ /**
8
+ * A simple ESBuild plugin fixing extensions for `require` and `import` calls.
9
+ *
10
+ * This can be useful when compiling dual-module packages (`esm` and `cjs`),
11
+ * where the file module type is determined by the `.mjs` or `.cjs` extension.
12
+ *
13
+ * For example this will make sure all `import` statements use the `.mjs`
14
+ * extensions, while all `require` use `.cjs`.
15
+ *
16
+ * ```
17
+ * await find('*.ts', { directory: 'src' })
18
+ * .esbuild({
19
+ * outdir: 'dist',
20
+ * format: 'cjs',
21
+ * plugins: [ fixExtensions ],
22
+ * outExtension: { '.js': '.mjs' },
23
+ * })
24
+ *
25
+ * await find('*.ts', { directory: 'src' })
26
+ * .esbuild({
27
+ * outdir: 'dist',
28
+ * format: 'esm',
29
+ * plugins: [ fixExtensions ],
30
+ * outExtension: { '.js': '.mjs' },
31
+ * })
32
+ * ```
33
+ */
34
+ export function fixExtensions(): Plugin {
35
+ return {
36
+ name: 'fix-extensions',
37
+
38
+ setup(build): void {
39
+ /* When using this, we fake esbuild's "bundle" functionality */
40
+ build.initialOptions.bundle = true
41
+
42
+ /* Our ".js" extension, might be remapped by `outExtension`s */
43
+ const cjs = build.initialOptions.outExtension?.['.cjs'] || '.cjs'
44
+ const mjs = build.initialOptions.outExtension?.['.mjs'] || '.mjs'
45
+ const js = build.initialOptions.outExtension?.['.js'] || '.js'
46
+
47
+ /* Extensions for files to look for */
48
+ const exts = build.initialOptions.resolveExtensions || [ '.ts', '.js', '.tsx', '.jsx' ]
49
+
50
+ /* Intercept resolution */
51
+ build.onResolve({ filter: /.*/ }, async (args) => {
52
+ /* Ignore the entry points (when the file is not being imported) */
53
+ if (! args.importer) return null
54
+
55
+ /* Anything not starting with "."? external node module */
56
+ if (! args.path.match(/^\.\.?\//)) return { external: true }
57
+
58
+ /* Some easy pathing options */
59
+ const resolveDir = args.resolveDir
60
+ assertAbsolutePath(resolveDir)
61
+
62
+ /* First of all, check if the _real_ filename exists */
63
+ const resolved = resolveAbsolutePath(resolveDir, args.path)
64
+ if (isFile(resolved)) return { path: args.path, external: true }
65
+
66
+ /*
67
+ * Thank you TypeScript 4.7!!! If the file is ".js", ".mjs" or ".cjs" we
68
+ * need to check if we have the corresponding ".ts", ".mts" or ".cjs"
69
+ * and return whatever ESBuild maps that particular extension to.
70
+ */
71
+ const match = args.path.match(/(.*)(\.[mc]?js$)/)
72
+ if (match) {
73
+ const [ , name, ext ] = match
74
+ const tspath = name + ext.replace('js', 'ts')
75
+ const tsfile = resolveAbsolutePath(resolveDir, tspath)
76
+ if (isFile(tsfile)) {
77
+ const newext = ext === '.mjs' ? mjs : ext === '.cjs' ? cjs : js
78
+ return { path: name + newext, external: true }
79
+ }
80
+ }
81
+
82
+ /* Check if ".../filename.ext" exists in our sources */
83
+ for (const ext of exts) {
84
+ const fileName = `${args.path}${ext}`
85
+ const filePath = path.resolve(args.resolveDir, fileName)
86
+ const isFile = await stat(filePath).then((stat) => stat.isFile(), (error) => void error)
87
+ if (isFile) return { path: `${args.path}${js}`, external: true }
88
+ }
89
+
90
+ /* If ".../filename" is not a directory, we end here */
91
+ const dirPath = path.resolve(args.resolveDir, args.path)
92
+ const isDir = await stat(dirPath).then((stat) => stat.isDirectory(), (error) => void error)
93
+ if (! isDir) return { external: true }
94
+
95
+ /* Check if ".../filename/index.ext" exists in our sources */
96
+ for (const ext of exts) {
97
+ const fileName = path.join(args.path, `index${ext}`)
98
+ const filePath = path.resolve(args.resolveDir, fileName)
99
+ const isFile = await stat(filePath).then((stat) => stat.isFile(), (error) => void error)
100
+ if (isFile) return { path: `${args.path}/index${js}`, external: true }
101
+ }
102
+
103
+ /* Nothing was found, then just mark this external */
104
+ return { external: true }
105
+ })
106
+ },
107
+ }
108
+ }
@@ -0,0 +1,128 @@
1
+ import { build, BuildFailure, BuildOptions, BuildResult, Message, Metafile } from 'esbuild'
2
+ import { assert } from '../assert.js'
3
+ import { Files, FilesBuilder } from '../files.js'
4
+ import { $p, ERROR, ReportLevel, ReportRecord, WARN } from '../log.js'
5
+ import { AbsolutePath, resolveAbsolutePath } from '../paths.js'
6
+ import { install, Plug } from '../pipe.js'
7
+ import { Run } from '../run.js'
8
+
9
+ export type ESBuildOptions = Omit<BuildOptions, 'absWorkingDir' | 'entryPoints' | 'watch'>
10
+
11
+ /**
12
+ * Transpile and bundle files with {@link https://esbuild.github.io/ | esbuild}.
13
+ */
14
+ export class ESBuild implements Plug<Files> {
15
+ constructor(options: ESBuildOptions)
16
+ constructor(private readonly _options: ESBuildOptions) {}
17
+
18
+ async pipe(files: Files, run: Run): Promise<Files> {
19
+ const entryPoints = [ ...files ]
20
+ const absWorkingDir = files.directory
21
+
22
+ const options: BuildOptions = {
23
+ /* Defaults */
24
+ platform: 'node',
25
+ target: `node${process.versions['node']}`,
26
+ format: 'cjs',
27
+ outbase: absWorkingDir,
28
+
29
+ logLevel: 'silent',
30
+
31
+ /* Our options */
32
+ ...this._options,
33
+
34
+ /* Always override */
35
+ absWorkingDir,
36
+ entryPoints,
37
+ watch: false,
38
+ }
39
+
40
+ /* Sanity check on output file/directory */
41
+ assert(!(options.outdir && options.outfile), 'Options "outfile" and "outdir" can not coexist')
42
+
43
+ /* Where to write, where to write? */
44
+ let builder: FilesBuilder
45
+ if (options.bundle && options.outfile && (entryPoints.length === 1)) {
46
+ builder = run.files(absWorkingDir)
47
+ const outputFile = resolveAbsolutePath(absWorkingDir, options.outfile)
48
+ const entryPoint = resolveAbsolutePath(absWorkingDir, entryPoints[0])
49
+ options.outfile = outputFile
50
+
51
+ run.log.debug('Bundling', $p(entryPoint), 'into', $p(outputFile))
52
+ } else {
53
+ assert(options.outdir, 'Option "outdir" must be specified')
54
+
55
+ builder = run.files(options.outdir)
56
+ options.outdir = builder.directory
57
+
58
+ const message = options.bundle ? 'Bundling' : 'Transpiling'
59
+ run.log.debug(message, entryPoints.length, 'files to', $p(builder.directory))
60
+ }
61
+
62
+ const report = run.report('ESBuild Report')
63
+
64
+ run.log.trace('Running ESBuild', options)
65
+ let esbuild: undefined | (BuildResult & { metafile: Metafile })
66
+ try {
67
+ esbuild = await build({ ...options, metafile: true })
68
+ run.log.trace('ESBuild Results', esbuild)
69
+
70
+ report.add(...esbuild.warnings.map((m) => convertMessage(WARN, m, absWorkingDir)))
71
+ report.add(...esbuild.errors.map((m) => convertMessage(ERROR, m, absWorkingDir)))
72
+ } catch (error: any) {
73
+ const e = error as BuildFailure
74
+ if (e.warnings) report.add(...e.warnings.map((m) => convertMessage(WARN, m, absWorkingDir)))
75
+ if (e.errors) report.add(...e.errors.map((m) => convertMessage(ERROR, m, absWorkingDir)))
76
+ }
77
+
78
+ await report.loadSources()
79
+ report.done()
80
+
81
+ assert(esbuild, 'ESBuild did not produce any result')
82
+
83
+ for (const file in esbuild.metafile.outputs) {
84
+ builder.add(resolveAbsolutePath(absWorkingDir, file))
85
+ }
86
+
87
+ const result = builder.build()
88
+ run.log.info('ESBuild produced', result.length, 'files into', $p(result.directory))
89
+ return result
90
+ }
91
+ }
92
+
93
+ function convertMessage(level: ReportLevel, message: Message, directory: AbsolutePath): ReportRecord {
94
+ const record: ReportRecord = { level, message: message.text }
95
+ record.tags = [ message.id, message.pluginName ].filter((tag) => !! tag)
96
+
97
+ if (message.location) {
98
+ record.line = message.location.line,
99
+ record.column = message.location.column + 1
100
+ record.length = message.location.length
101
+ record.file = resolveAbsolutePath(directory, message.location.file)
102
+ }
103
+
104
+ return record
105
+ }
106
+
107
+ /* ========================================================================== *
108
+ * INSTALLATION *
109
+ * ========================================================================== */
110
+
111
+ install('esbuild', ESBuild)
112
+
113
+ declare module '../pipe.js' {
114
+ export interface Pipe {
115
+ /**
116
+ * Transpile and bundle files with {@link https://esbuild.github.io/ esbuild}.
117
+ */
118
+ esbuild: PipeExtension<typeof ESBuild>
119
+ }
120
+ }
121
+
122
+ /* ========================================================================== *
123
+ * PLUGINS *
124
+ * ========================================================================== */
125
+
126
+ export * from './esbuild/bundle-locals.js'
127
+ export * from './esbuild/check-dependencies.js'
128
+ export * from './esbuild/fix-extensions.js'
@@ -0,0 +1,112 @@
1
+ import { ESLint as RealESLint } from 'eslint'
2
+ import { assert, failure } from '../../assert.js'
3
+ import { Files } from '../../files.js'
4
+ import { $p, ERROR, NOTICE, WARN } from '../../log.js'
5
+ import { getCurrentWorkingDirectory, isDirectory, isFile, resolveAbsolutePath } from '../../paths.js'
6
+ import { Plug } from '../../pipe.js'
7
+ import { Run } from '../../run.js'
8
+ import { readFile } from '../../utils/asyncfs.js'
9
+
10
+ export interface ESLintOptions {
11
+ /** ESLint's own _current working directory_, where config files are. */
12
+ directory?: string
13
+ /** Show sources in report? */
14
+ showSources?: boolean
15
+ /**
16
+ * ESLint's _override_ configuration file: configurations specified in this
17
+ * file will override any other configuration specified elsewhere.
18
+ */
19
+ configFile?: string
20
+ }
21
+
22
+ /** Writes some info about the current {@link Files} being passed around. */
23
+ export default class ESLint implements Plug<undefined> {
24
+ private readonly _options: Readonly<ESLintOptions>
25
+
26
+ constructor()
27
+ constructor(configFile: string)
28
+ constructor(options: ESLintOptions)
29
+ constructor(arg: string | ESLintOptions = {}) {
30
+ this._options = typeof arg === 'string' ? { configFile: arg } : arg
31
+ }
32
+
33
+ async pipe(files: Files, run: Run): Promise<undefined> {
34
+ const { directory, configFile } = this._options
35
+
36
+ const cwd = directory ? run.resolve(directory) : getCurrentWorkingDirectory()
37
+ assert(isDirectory(cwd), `ESLint directory ${$p(cwd)} does not exist`)
38
+
39
+ const overrideConfigFile = configFile ? run.resolve(configFile) : undefined
40
+ if (overrideConfigFile) {
41
+ assert(isFile(overrideConfigFile), `ESLint configuration ${$p(overrideConfigFile)} does not exist`)
42
+ }
43
+
44
+ /* Create our ESLint instance */
45
+ const eslint = new RealESLint({ overrideConfigFile, cwd })
46
+
47
+ /* Lint all files in parallel */
48
+ const paths = [ ...files.absolutePaths() ]
49
+ const promises = paths.map(async (filePath) => {
50
+ const code = await readFile(filePath, 'utf-8')
51
+ return eslint.lintText(code, { filePath })
52
+ })
53
+
54
+ /* Await for all promises to be settled */
55
+ const settlements = await Promise.allSettled(promises)
56
+
57
+ /* Run through all promises settlements */
58
+ const summary = settlements.reduce((summary, settlement, i) => {
59
+ /* Promise rejected, meaining hard failure */
60
+ if (settlement.status === 'rejected') {
61
+ run.log.error('Error linting', $p(paths[i]), settlement.reason)
62
+ summary.failures ++
63
+ return summary
64
+ }
65
+
66
+ /* Push all our results in the summary */
67
+ summary.results.push(...settlement.value)
68
+ return summary
69
+ }, {
70
+ results: [] as RealESLint.LintResult[],
71
+ failures: 0,
72
+ })
73
+
74
+ /* In case of failures from promises, fail! */
75
+ const { results, failures } = summary
76
+ if (failures) throw failure() // already logged above
77
+
78
+ /* Create our report */
79
+ const report = run.report('ESLint Report')
80
+
81
+ /* Convert ESLint results into our report records */
82
+ for (const result of results) {
83
+ const { filePath, source, messages } = result
84
+ const file = resolveAbsolutePath(getCurrentWorkingDirectory(), filePath)
85
+
86
+ for (const record of messages) {
87
+ const {
88
+ severity,
89
+ message,
90
+ ruleId: tags,
91
+ line,
92
+ column,
93
+ endLine = line,
94
+ endColumn = column + 1,
95
+ } = record
96
+
97
+ /* Severity becomes our "kind" */
98
+ const level = severity === 0 ? NOTICE : severity === 1 ? WARN : ERROR
99
+
100
+ /* Characters */
101
+ const length = endLine === line ? endColumn - column : endLine > line ? -1 : 1
102
+
103
+ /* Add our report */
104
+ report.add({ level, message, tags, line, column, length, file, source })
105
+ }
106
+ }
107
+
108
+ /* Emit our report and fail on errors */
109
+ report.done(this._options.showSources)
110
+ return undefined
111
+ }
112
+ }