@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.
- package/LICENSE.md +211 -0
- package/NOTICE.md +13 -0
- package/README.md +7 -0
- package/dist/assert.cjs +72 -0
- package/dist/assert.cjs.map +6 -0
- package/dist/assert.mjs +41 -0
- package/dist/assert.mjs.map +6 -0
- package/dist/async.cjs +58 -0
- package/dist/async.cjs.map +6 -0
- package/dist/async.mjs +30 -0
- package/dist/async.mjs.map +6 -0
- package/dist/build.cjs +136 -0
- package/dist/build.cjs.map +6 -0
- package/dist/build.mjs +110 -0
- package/dist/build.mjs.map +6 -0
- package/dist/files.cjs +113 -0
- package/dist/files.cjs.map +6 -0
- package/dist/files.mjs +88 -0
- package/dist/files.mjs.map +6 -0
- package/dist/fork.cjs +177 -0
- package/dist/fork.cjs.map +6 -0
- package/dist/fork.mjs +151 -0
- package/dist/fork.mjs.map +6 -0
- package/dist/helpers.cjs +129 -0
- package/dist/helpers.cjs.map +6 -0
- package/dist/helpers.mjs +97 -0
- package/dist/helpers.mjs.map +6 -0
- package/dist/index.cjs +25 -0
- package/dist/index.cjs.map +6 -0
- package/dist/index.mjs +7 -0
- package/dist/index.mjs.map +6 -0
- package/dist/log/colors.cjs +129 -0
- package/dist/log/colors.cjs.map +6 -0
- package/dist/log/colors.mjs +93 -0
- package/dist/log/colors.mjs.map +6 -0
- package/dist/log/emit.cjs +109 -0
- package/dist/log/emit.cjs.map +6 -0
- package/dist/log/emit.mjs +83 -0
- package/dist/log/emit.mjs.map +6 -0
- package/dist/log/levels.cjs +75 -0
- package/dist/log/levels.cjs.map +6 -0
- package/dist/log/levels.mjs +41 -0
- package/dist/log/levels.mjs.map +6 -0
- package/dist/log/logger.cjs +129 -0
- package/dist/log/logger.cjs.map +6 -0
- package/dist/log/logger.mjs +104 -0
- package/dist/log/logger.mjs.map +6 -0
- package/dist/log/options.cjs +149 -0
- package/dist/log/options.cjs.map +6 -0
- package/dist/log/options.mjs +124 -0
- package/dist/log/options.mjs.map +6 -0
- package/dist/log/report.cjs +284 -0
- package/dist/log/report.cjs.map +6 -0
- package/dist/log/report.mjs +259 -0
- package/dist/log/report.mjs.map +6 -0
- package/dist/log/spinner.cjs +83 -0
- package/dist/log/spinner.cjs.map +6 -0
- package/dist/log/spinner.mjs +57 -0
- package/dist/log/spinner.mjs.map +6 -0
- package/dist/log.cjs +71 -0
- package/dist/log.cjs.map +6 -0
- package/dist/log.mjs +45 -0
- package/dist/log.mjs.map +6 -0
- package/dist/paths.cjs +158 -0
- package/dist/paths.cjs.map +6 -0
- package/dist/paths.mjs +122 -0
- package/dist/paths.mjs.map +6 -0
- package/dist/pipe.cjs +71 -0
- package/dist/pipe.cjs.map +6 -0
- package/dist/pipe.mjs +44 -0
- package/dist/pipe.mjs.map +6 -0
- package/dist/plugs/copy.cjs +95 -0
- package/dist/plugs/copy.cjs.map +6 -0
- package/dist/plugs/copy.mjs +64 -0
- package/dist/plugs/copy.mjs.map +6 -0
- package/dist/plugs/coverage/analysis.cjs +229 -0
- package/dist/plugs/coverage/analysis.cjs.map +6 -0
- package/dist/plugs/coverage/analysis.mjs +202 -0
- package/dist/plugs/coverage/analysis.mjs.map +6 -0
- package/dist/plugs/coverage/report.cjs +215 -0
- package/dist/plugs/coverage/report.cjs.map +6 -0
- package/dist/plugs/coverage/report.mjs +200 -0
- package/dist/plugs/coverage/report.mjs.map +6 -0
- package/dist/plugs/coverage.cjs +142 -0
- package/dist/plugs/coverage.cjs.map +6 -0
- package/dist/plugs/coverage.mjs +117 -0
- package/dist/plugs/coverage.mjs.map +6 -0
- package/dist/plugs/debug.cjs +50 -0
- package/dist/plugs/debug.cjs.map +6 -0
- package/dist/plugs/debug.mjs +25 -0
- package/dist/plugs/debug.mjs.map +6 -0
- package/dist/plugs/esbuild/bundle-locals.cjs +51 -0
- package/dist/plugs/esbuild/bundle-locals.cjs.map +6 -0
- package/dist/plugs/esbuild/bundle-locals.mjs +26 -0
- package/dist/plugs/esbuild/bundle-locals.mjs.map +6 -0
- package/dist/plugs/esbuild/check-dependencies.cjs +140 -0
- package/dist/plugs/esbuild/check-dependencies.cjs.map +6 -0
- package/dist/plugs/esbuild/check-dependencies.mjs +115 -0
- package/dist/plugs/esbuild/check-dependencies.mjs.map +6 -0
- package/dist/plugs/esbuild/fix-extensions.cjs +91 -0
- package/dist/plugs/esbuild/fix-extensions.cjs.map +6 -0
- package/dist/plugs/esbuild/fix-extensions.mjs +60 -0
- package/dist/plugs/esbuild/fix-extensions.mjs.map +6 -0
- package/dist/plugs/esbuild.cjs +109 -0
- package/dist/plugs/esbuild.cjs.map +6 -0
- package/dist/plugs/esbuild.mjs +83 -0
- package/dist/plugs/esbuild.mjs.map +6 -0
- package/dist/plugs/eslint/runner.cjs +91 -0
- package/dist/plugs/eslint/runner.cjs.map +6 -0
- package/dist/plugs/eslint/runner.mjs +68 -0
- package/dist/plugs/eslint/runner.mjs.map +6 -0
- package/dist/plugs/exec.cjs +128 -0
- package/dist/plugs/exec.cjs.map +6 -0
- package/dist/plugs/exec.mjs +96 -0
- package/dist/plugs/exec.mjs.map +6 -0
- package/dist/plugs/filter.cjs +59 -0
- package/dist/plugs/filter.cjs.map +6 -0
- package/dist/plugs/filter.mjs +34 -0
- package/dist/plugs/filter.mjs.map +6 -0
- package/dist/plugs/mocha/reporter.cjs +140 -0
- package/dist/plugs/mocha/reporter.cjs.map +6 -0
- package/dist/plugs/mocha/reporter.mjs +107 -0
- package/dist/plugs/mocha/reporter.mjs.map +6 -0
- package/dist/plugs/mocha/runner.cjs +73 -0
- package/dist/plugs/mocha/runner.cjs.map +6 -0
- package/dist/plugs/mocha/runner.mjs +44 -0
- package/dist/plugs/mocha/runner.mjs.map +6 -0
- package/dist/plugs/tsc/compiler.cjs +74 -0
- package/dist/plugs/tsc/compiler.cjs.map +6 -0
- package/dist/plugs/tsc/compiler.mjs +43 -0
- package/dist/plugs/tsc/compiler.mjs.map +6 -0
- package/dist/plugs/tsc/options.cjs +82 -0
- package/dist/plugs/tsc/options.cjs.map +6 -0
- package/dist/plugs/tsc/options.mjs +51 -0
- package/dist/plugs/tsc/options.mjs.map +6 -0
- package/dist/plugs/tsc/report.cjs +90 -0
- package/dist/plugs/tsc/report.cjs.map +6 -0
- package/dist/plugs/tsc/report.mjs +59 -0
- package/dist/plugs/tsc/report.mjs.map +6 -0
- package/dist/plugs/tsc/runner.cjs +101 -0
- package/dist/plugs/tsc/runner.cjs.map +6 -0
- package/dist/plugs/tsc/runner.mjs +72 -0
- package/dist/plugs/tsc/runner.mjs.map +6 -0
- package/dist/plugs.cjs +31 -0
- package/dist/plugs.cjs.map +6 -0
- package/dist/plugs.mjs +13 -0
- package/dist/plugs.mjs.map +6 -0
- package/dist/run.cjs +95 -0
- package/dist/run.cjs.map +6 -0
- package/dist/run.mjs +70 -0
- package/dist/run.mjs.map +6 -0
- package/dist/task.cjs +39 -0
- package/dist/task.cjs.map +6 -0
- package/dist/task.mjs +14 -0
- package/dist/task.mjs.map +6 -0
- package/dist/utils/asyncfs.cjs +143 -0
- package/dist/utils/asyncfs.cjs.map +6 -0
- package/dist/utils/asyncfs.mjs +83 -0
- package/dist/utils/asyncfs.mjs.map +6 -0
- package/dist/utils/caller.cjs +59 -0
- package/dist/utils/caller.cjs.map +6 -0
- package/dist/utils/caller.mjs +34 -0
- package/dist/utils/caller.mjs.map +6 -0
- package/dist/utils/match.cjs +69 -0
- package/dist/utils/match.cjs.map +6 -0
- package/dist/utils/match.mjs +38 -0
- package/dist/utils/match.mjs.map +6 -0
- package/dist/utils/options.cjs +41 -0
- package/dist/utils/options.cjs.map +6 -0
- package/dist/utils/options.mjs +16 -0
- package/dist/utils/options.mjs.map +6 -0
- package/dist/utils/walk.cjs +104 -0
- package/dist/utils/walk.cjs.map +6 -0
- package/dist/utils/walk.mjs +79 -0
- package/dist/utils/walk.mjs.map +6 -0
- package/extra/cli.mjs +212 -0
- package/extra/ts-loader.mjs +214 -0
- package/extra/webassembly.d.ts +11 -0
- package/package.json +57 -0
- package/src/assert.ts +47 -0
- package/src/async.ts +50 -0
- package/src/files.ts +129 -0
- package/src/fork.ts +263 -0
- package/src/helpers.ts +145 -0
- package/src/index.ts +20 -0
- package/src/log/colors.ts +119 -0
- package/src/log/emit.ts +125 -0
- package/src/log/levels.ts +65 -0
- package/src/log/logger.ts +171 -0
- package/src/log/options.ts +199 -0
- package/src/log/report.ts +433 -0
- package/src/log/spinner.ts +70 -0
- package/src/log.ts +68 -0
- package/src/paths.ts +213 -0
- package/src/pipe.ts +231 -0
- package/src/plugs/copy.ts +113 -0
- package/src/plugs/coverage/analysis.ts +395 -0
- package/src/plugs/coverage/report.ts +337 -0
- package/src/plugs/coverage.ts +194 -0
- package/src/plugs/debug.ts +35 -0
- package/src/plugs/esbuild/bundle-locals.ts +33 -0
- package/src/plugs/esbuild/check-dependencies.ts +158 -0
- package/src/plugs/esbuild/fix-extensions.ts +108 -0
- package/src/plugs/esbuild.ts +128 -0
- package/src/plugs/eslint/runner.ts +112 -0
- package/src/plugs/exec.ts +215 -0
- package/src/plugs/filter.ts +56 -0
- package/src/plugs/mocha/reporter.ts +152 -0
- package/src/plugs/mocha/runner.ts +77 -0
- package/src/plugs/tsc/compiler.ts +66 -0
- package/src/plugs/tsc/options.ts +97 -0
- package/src/plugs/tsc/report.ts +74 -0
- package/src/plugs/tsc/runner.ts +100 -0
- package/src/plugs.ts +33 -0
- package/src/run.ts +160 -0
- package/src/task.ts +26 -0
- package/src/utils/asyncfs.ts +82 -0
- package/src/utils/caller.ts +45 -0
- package/src/utils/match.ts +286 -0
- package/src/utils/options.ts +22 -0
- package/src/utils/walk.ts +136 -0
- package/types/assert.d.ts +18 -0
- package/types/async.d.ts +20 -0
- package/types/build.d.ts +56 -0
- package/types/files.d.ts +44 -0
- package/types/fork.d.ts +57 -0
- package/types/helpers.d.ts +49 -0
- package/types/index.d.ts +14 -0
- package/types/log/colors.d.ts +25 -0
- package/types/log/emit.d.ts +14 -0
- package/types/log/levels.d.ts +52 -0
- package/types/log/logger.d.ts +31 -0
- package/types/log/options.d.ts +40 -0
- package/types/log/report.d.ts +64 -0
- package/types/log/spinner.d.ts +2 -0
- package/types/log.d.ts +10 -0
- package/types/paths.d.ts +76 -0
- package/types/pipe.d.ts +152 -0
- package/types/plugs/copy.d.ts +27 -0
- package/types/plugs/coverage/analysis.d.ts +104 -0
- package/types/plugs/coverage/report.d.ts +53 -0
- package/types/plugs/coverage.d.ts +46 -0
- package/types/plugs/debug.d.ts +14 -0
- package/types/plugs/esbuild/bundle-locals.d.ts +6 -0
- package/types/plugs/esbuild/check-dependencies.d.ts +12 -0
- package/types/plugs/esbuild/fix-extensions.d.ts +29 -0
- package/types/plugs/esbuild.d.ts +24 -0
- package/types/plugs/eslint/runner.d.ts +22 -0
- package/types/plugs/exec.d.ts +90 -0
- package/types/plugs/filter.d.ts +23 -0
- package/types/plugs/mocha/reporter.d.ts +8 -0
- package/types/plugs/mocha/runner.d.ts +34 -0
- package/types/plugs/tsc/compiler.d.ts +24 -0
- package/types/plugs/tsc/options.d.ts +8 -0
- package/types/plugs/tsc/report.d.ts +5 -0
- package/types/plugs/tsc/runner.d.ts +13 -0
- package/types/plugs.d.ts +16 -0
- package/types/run.d.ts +89 -0
- package/types/task.d.ts +15 -0
- package/types/utils/asyncfs.d.ts +37 -0
- package/types/utils/caller.d.ts +7 -0
- package/types/utils/match.d.ts +216 -0
- package/types/utils/options.d.ts +15 -0
- 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
|
+
}
|