@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
package/src/paths.ts
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { statSync } from 'node:fs'
|
|
2
|
+
import { createRequire } from 'node:module'
|
|
3
|
+
import { dirname, extname, isAbsolute, join, relative, resolve, sep } from 'node:path'
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
5
|
+
import { assert } from './assert.js'
|
|
6
|
+
|
|
7
|
+
/** A _branded_ `string` representing an _absolute_ path name */
|
|
8
|
+
export type AbsolutePath = string & { __brand_absolute_path: never }
|
|
9
|
+
|
|
10
|
+
/* ========================================================================== *
|
|
11
|
+
* PATH FUNCTIONS *
|
|
12
|
+
* ========================================================================== */
|
|
13
|
+
|
|
14
|
+
/** Resolve a path into an {@link AbsolutePath} */
|
|
15
|
+
export function resolveAbsolutePath(directory: AbsolutePath, ...paths: string[]): AbsolutePath {
|
|
16
|
+
const resolved = resolve(directory, ...paths) as AbsolutePath
|
|
17
|
+
assert(isAbsolute(resolved), `Path "${join(...paths)}" resolved in "${directory}" is not absolute`)
|
|
18
|
+
return resolved
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Resolve a path as a relative path to the directory specified, returning the
|
|
23
|
+
* relative child path or `undefined` if the specified path was not a child.
|
|
24
|
+
*
|
|
25
|
+
* The `path` specified here _could_ also be another {@link AbsolutePath}
|
|
26
|
+
* therefore something like this will work:
|
|
27
|
+
*
|
|
28
|
+
* ```
|
|
29
|
+
* resolveRelativeChildPath('/foo', '/foo/bar')
|
|
30
|
+
* // will yield `bar`
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export function resolveRelativeChildPath(directory: AbsolutePath, ...paths: string[]): string | undefined {
|
|
34
|
+
assertAbsolutePath(directory)
|
|
35
|
+
|
|
36
|
+
const abs = resolveAbsolutePath(directory, ...paths)
|
|
37
|
+
const rel = relative(directory, abs)
|
|
38
|
+
return (isAbsolute(rel) || (rel === '..') || rel.startsWith(`..${sep}`)) ? undefined : rel
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Asserts that a path is a relative path to the directory specified, failing
|
|
43
|
+
* the build if it's not (see also {@link resolveRelativeChildPath}).
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
export function assertRelativeChildPath(directory: AbsolutePath, ...paths: string[]): string {
|
|
47
|
+
const relative = resolveRelativeChildPath(directory, ...paths)
|
|
48
|
+
assert(relative, `Path "${join(...paths)}" not relative to "${directory}"`)
|
|
49
|
+
return relative
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Checks that the specified path is an {@link AbsolutePath} */
|
|
53
|
+
export function isAbsolutePath(path: string): path is AbsolutePath {
|
|
54
|
+
return isAbsolute(path)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Asserts that the specified path is an {@link AbsolutePath} */
|
|
58
|
+
export function assertAbsolutePath(p: string): asserts p is AbsolutePath {
|
|
59
|
+
assert(isAbsolute(p), `Path "${p}" not absolute`)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Return the {@link AbsolutePath} parent of another */
|
|
63
|
+
export function getAbsoluteParent(path: AbsolutePath): AbsolutePath {
|
|
64
|
+
assertAbsolutePath(path)
|
|
65
|
+
return dirname(path) as AbsolutePath
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Return the {@link process.cwd() | current working directory} as an
|
|
70
|
+
* {@link AbsolutePath}.
|
|
71
|
+
*/
|
|
72
|
+
export function getCurrentWorkingDirectory(): AbsolutePath {
|
|
73
|
+
const cwd = process.cwd()
|
|
74
|
+
assertAbsolutePath(cwd)
|
|
75
|
+
return cwd
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* ========================================================================== *
|
|
79
|
+
* MODULE RESOLUTION FUNCTIONS *
|
|
80
|
+
* ========================================================================== */
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Return the absolute path of a file relative to the given `__fileurl`, where
|
|
84
|
+
* `__fileurl` is either CommonJS's own `__filename` variable, or EcmaScript's
|
|
85
|
+
* `import.meta.url` (so either an absolute path name, or a `file:///...` url).
|
|
86
|
+
*
|
|
87
|
+
* If further `paths` are specified, those will be resolved as relative paths
|
|
88
|
+
* to the original `__fileurl` so we can easily write something like this:
|
|
89
|
+
*
|
|
90
|
+
* ```
|
|
91
|
+
* const dataFile = resolveFilename(__fileurl, 'data.json')
|
|
92
|
+
* // if we write this in "/foo/bar/baz.(ts|js|cjs|mjs)"
|
|
93
|
+
* // `dataFile` will now be "/foo/bar/data.json"
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
export function requireFilename(__fileurl: string, ...paths: string[]): AbsolutePath {
|
|
97
|
+
/* Convert any "file:..." URL into a path name */
|
|
98
|
+
const file = __fileurl.startsWith('file:') ? fileURLToPath(__fileurl) : __fileurl
|
|
99
|
+
|
|
100
|
+
/* We should really have a proper absolute file name now */
|
|
101
|
+
assertAbsolutePath(file)
|
|
102
|
+
|
|
103
|
+
/* No paths? Return the file! */
|
|
104
|
+
if (! paths.length) return file
|
|
105
|
+
|
|
106
|
+
/* Resolve any paths, relative to the file */
|
|
107
|
+
const directory = getAbsoluteParent(file)
|
|
108
|
+
return resolveAbsolutePath(directory, ...paths)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Return the absolute path of a file which can be _required_ or _imported_
|
|
113
|
+
* by Node (or forked to via `child_process.fork`).
|
|
114
|
+
*
|
|
115
|
+
* This leverages {@link requireFilename} to figure out the starting point where
|
|
116
|
+
* to look for files, and will _try_ to match the same extension of `__fileurl`
|
|
117
|
+
* (so, `.ts` for `ts-node`, `.mjs` for ESM modules, ...).
|
|
118
|
+
*/
|
|
119
|
+
export function requireResolve(__fileurl: string, module: string): AbsolutePath {
|
|
120
|
+
const file = requireFilename(__fileurl)
|
|
121
|
+
|
|
122
|
+
// We do our custom resolution _only_ for local (./foo.bar) files...
|
|
123
|
+
if (module.match(/^\.\.?\//)) {
|
|
124
|
+
// If we import "../foo.ext" from "/a/b/c/bar.ts" we need to check:
|
|
125
|
+
// * /a/b/foo.ext
|
|
126
|
+
// * /a/b/foo.ext.ts
|
|
127
|
+
// * /a/b/foo.ext/index.ts
|
|
128
|
+
// ... then delegate to the standard "require.resolve(...)"
|
|
129
|
+
const url = pathToFileURL(file)
|
|
130
|
+
const ext = extname(file)
|
|
131
|
+
const checks = ext ? [ `${module}`, `${module}${ext}`, `${module}/index${ext}` ] : [ module ]
|
|
132
|
+
|
|
133
|
+
for (const check of checks) {
|
|
134
|
+
const resolved = fileURLToPath(new URL(check, url)) as AbsolutePath
|
|
135
|
+
if (isFile(resolved)) {
|
|
136
|
+
module = check
|
|
137
|
+
break
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const require = createRequire(file)
|
|
143
|
+
const required = require.resolve(module)
|
|
144
|
+
assertAbsolutePath(required)
|
|
145
|
+
return required
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Return the _common_ path amongst all specified paths.
|
|
150
|
+
*
|
|
151
|
+
* While the first `path` _must_ be an {@link AbsolutePath}, all other `paths`
|
|
152
|
+
* can be _relative_ and will be resolved against the first `path`.
|
|
153
|
+
*/
|
|
154
|
+
export function commonPath(path: AbsolutePath, ...paths: string[]): AbsolutePath {
|
|
155
|
+
assertAbsolutePath(path)
|
|
156
|
+
|
|
157
|
+
// Here the first path will be split into its components
|
|
158
|
+
// on win => [ 'C:', 'Windows', 'System32' ]
|
|
159
|
+
// on unx => [ '', 'usr'
|
|
160
|
+
const components = path.split(sep)
|
|
161
|
+
|
|
162
|
+
let length = components.length
|
|
163
|
+
for (const current of paths) {
|
|
164
|
+
const absolute = resolveAbsolutePath(path, current)
|
|
165
|
+
const parts = absolute.split(sep)
|
|
166
|
+
for (let i = 0; i < length; i++) {
|
|
167
|
+
if (components[i] !== parts[i]) {
|
|
168
|
+
length = i
|
|
169
|
+
break
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
assert(length, 'No common ancestors amongst paths')
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const common = components.slice(0, length).join(sep)
|
|
177
|
+
assertAbsolutePath(common)
|
|
178
|
+
return common
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/* ========================================================================== *
|
|
182
|
+
* FILE CHECKING FUNCTIONS *
|
|
183
|
+
* ========================================================================== */
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Resolves the specified path as an {@link AbsolutePath} and checks it is a
|
|
187
|
+
* _file_, returning `undefined` if it is not.
|
|
188
|
+
*/
|
|
189
|
+
export function isFile(path: AbsolutePath, ...paths: string[]): AbsolutePath | undefined {
|
|
190
|
+
const file = resolveAbsolutePath(path, ...paths)
|
|
191
|
+
try {
|
|
192
|
+
const stat = statSync(file)
|
|
193
|
+
if (stat.isFile()) return file
|
|
194
|
+
} catch (error: any) {
|
|
195
|
+
if (error.code !== 'ENOENT') throw error
|
|
196
|
+
}
|
|
197
|
+
return undefined
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Resolves the specified path as an {@link AbsolutePath} and checks it is a
|
|
202
|
+
* _directory_, returning `undefined` if it is not.
|
|
203
|
+
*/
|
|
204
|
+
export function isDirectory(path: AbsolutePath, ...paths: string[]): AbsolutePath | undefined {
|
|
205
|
+
const directory = resolveAbsolutePath(path, ...paths)
|
|
206
|
+
try {
|
|
207
|
+
const stat = statSync(directory)
|
|
208
|
+
if (stat.isDirectory()) return directory
|
|
209
|
+
} catch (error: any) {
|
|
210
|
+
if (error.code !== 'ENOENT') throw error
|
|
211
|
+
}
|
|
212
|
+
return undefined
|
|
213
|
+
}
|
package/src/pipe.ts
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { assert } from './assert.js'
|
|
2
|
+
import { Files } from './files.js'
|
|
3
|
+
import { Run } from './run.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The {@link Plug} interface describes an extension mechanism for our build.
|
|
7
|
+
*/
|
|
8
|
+
export interface Plug<T extends Files | undefined> {
|
|
9
|
+
pipe(files: Files, run: Run): T | Promise<T>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A type identifying a {@link Plug} as a `function`
|
|
14
|
+
*/
|
|
15
|
+
export type PlugFunction<T extends Files | undefined> = Plug<T>['pipe']
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A {@link Pipe} represents a sequence of operations performed by
|
|
19
|
+
* a series of {@link Plug | Plugs}.
|
|
20
|
+
*/
|
|
21
|
+
export interface Pipe {
|
|
22
|
+
/* Left empty, for definitions of installed plugs as extensions */
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The {@link Pipe} abstract class exposes the prototype upon which all
|
|
27
|
+
* extension plugs will be installed on.
|
|
28
|
+
*/
|
|
29
|
+
export abstract class Pipe implements Pipe {
|
|
30
|
+
abstract plug(plug: Plug<Files> | PlugFunction<Files>): Pipe & Promise<Files>
|
|
31
|
+
abstract plug(plug: Plug<undefined> | PlugFunction<undefined>): Promise<undefined>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
/** Implementation of our {@link Pipe}. */
|
|
36
|
+
export class PipeImpl<T extends Files | undefined> extends Pipe implements Promise<T> {
|
|
37
|
+
readonly #promise: Promise<T>
|
|
38
|
+
readonly #run: Run
|
|
39
|
+
|
|
40
|
+
constructor(start: T | Promise<T>, run: Run) {
|
|
41
|
+
super()
|
|
42
|
+
this.#promise = Promise.resolve(start)
|
|
43
|
+
this.#run = run
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
plug<T extends Files | undefined>(arg: Plug<T> | PlugFunction<T>): Pipe & Promise<T> {
|
|
47
|
+
const plug = typeof arg === 'function' ? { pipe: arg } : arg
|
|
48
|
+
const promise = this.#promise.then((files) => {
|
|
49
|
+
assert(files, 'Pipe can not be further extended')
|
|
50
|
+
return plug.pipe(files, this.#run)
|
|
51
|
+
})
|
|
52
|
+
return new PipeImpl(promise, this.#run)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
then<T1 = T, T2 = never>(
|
|
56
|
+
onfulfilled?: ((value: T) => T1 | PromiseLike<T1>) | null | undefined,
|
|
57
|
+
onrejected?: ((reason: any) => T2 | PromiseLike<T2>) | null | undefined,
|
|
58
|
+
): Promise<T1 | T2> {
|
|
59
|
+
return this.#promise.then(onfulfilled, onrejected)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
catch<T0 = never>(
|
|
63
|
+
onrejected?: ((reason: any) => T0 | PromiseLike<T0>) | null | undefined,
|
|
64
|
+
): Promise<T0 | T> {
|
|
65
|
+
return this.#promise.catch(onrejected)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
finally(
|
|
69
|
+
onfinally?: (() => void) | null | undefined,
|
|
70
|
+
): Promise<T> {
|
|
71
|
+
return this.#promise.finally(onfinally)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
[Symbol.toStringTag] = 'Pipe'
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* ========================================================================== *
|
|
78
|
+
* PLUG INSTALLATION (INTERNAL) *
|
|
79
|
+
* ========================================================================== */
|
|
80
|
+
|
|
81
|
+
/** The names which can be installed as direct plugs. */
|
|
82
|
+
export type PlugName = string & Exclude<keyof Pipe, 'plug' | keyof Promise<Files>>
|
|
83
|
+
|
|
84
|
+
/** A convenience type identifying a {@link Plug} constructor. */
|
|
85
|
+
export type PlugConstructor = new (...args: any) => Plug<Files | undefined>
|
|
86
|
+
|
|
87
|
+
/** Convert the resulting type of a {@link Plug} for use in a {@link Pipe} */
|
|
88
|
+
type PlugReturnForPipe<T> =
|
|
89
|
+
T extends Plug<infer R> ?
|
|
90
|
+
R extends Files ?
|
|
91
|
+
Promise<Files> & Pipe :
|
|
92
|
+
R extends undefined ?
|
|
93
|
+
Promise<undefined> :
|
|
94
|
+
never :
|
|
95
|
+
never
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Map constructors into an array of all known overloads.
|
|
99
|
+
*
|
|
100
|
+
* This is a _royal_ pain in the ass, as we need to distinguish between
|
|
101
|
+
* all possible number of overloads of a constructor... Limit to 5 of them!
|
|
102
|
+
*
|
|
103
|
+
* Also, the empty constructor (when specified in the overloads) will simply
|
|
104
|
+
* match the first case (most overloads) and generate functions somewhat like
|
|
105
|
+
*
|
|
106
|
+
* (...args: unknown[]) => never
|
|
107
|
+
* (...args: unknown[]) => never
|
|
108
|
+
* (...args: unknown[]) => never
|
|
109
|
+
* () => PlugReturnForPipe<R3>
|
|
110
|
+
* (arg: Options) => PlugReturnForPipe<R4>
|
|
111
|
+
*
|
|
112
|
+
* Somehow inferring the result to `Function` or the right type and ANDing all
|
|
113
|
+
* those together here doesn't work, so we create this array and we'll AND
|
|
114
|
+
* all its members in the PipeExtension<...> type.
|
|
115
|
+
*/
|
|
116
|
+
type PlugConstructorOverloads<T extends PlugConstructor> =
|
|
117
|
+
T extends {
|
|
118
|
+
new (...args: infer A0): infer R0
|
|
119
|
+
new (...args: infer A1): infer R1
|
|
120
|
+
new (...args: infer A2): infer R2
|
|
121
|
+
new (...args: infer A3): infer R3
|
|
122
|
+
new (...args: infer A4): infer R4
|
|
123
|
+
} ? [
|
|
124
|
+
R0 extends Plug<Files | undefined> ? ((...args: A0) => PlugReturnForPipe<R0>) : Function,
|
|
125
|
+
R1 extends Plug<Files | undefined> ? ((...args: A1) => PlugReturnForPipe<R1>) : Function,
|
|
126
|
+
R2 extends Plug<Files | undefined> ? ((...args: A2) => PlugReturnForPipe<R2>) : Function,
|
|
127
|
+
R3 extends Plug<Files | undefined> ? ((...args: A3) => PlugReturnForPipe<R3>) : Function,
|
|
128
|
+
R4 extends Plug<Files | undefined> ? ((...args: A4) => PlugReturnForPipe<R4>) : Function,
|
|
129
|
+
] :
|
|
130
|
+
T extends {
|
|
131
|
+
new (...args: infer A0): infer R0
|
|
132
|
+
new (...args: infer A1): infer R1
|
|
133
|
+
new (...args: infer A2): infer R2
|
|
134
|
+
new (...args: infer A3): infer R3
|
|
135
|
+
} ? [
|
|
136
|
+
R0 extends Plug<Files | undefined> ? (...args: A0) => PlugReturnForPipe<R0> : Function,
|
|
137
|
+
R1 extends Plug<Files | undefined> ? (...args: A1) => PlugReturnForPipe<R1> : Function,
|
|
138
|
+
R2 extends Plug<Files | undefined> ? (...args: A2) => PlugReturnForPipe<R2> : Function,
|
|
139
|
+
R3 extends Plug<Files | undefined> ? (...args: A3) => PlugReturnForPipe<R3> : Function,
|
|
140
|
+
] :
|
|
141
|
+
T extends {
|
|
142
|
+
new (...args: infer A0): infer R0
|
|
143
|
+
new (...args: infer A1): infer R1
|
|
144
|
+
new (...args: infer A2): infer R2
|
|
145
|
+
} ? [
|
|
146
|
+
R0 extends Plug<Files | undefined> ? (...args: A0) => PlugReturnForPipe<R0> : Function,
|
|
147
|
+
R1 extends Plug<Files | undefined> ? (...args: A1) => PlugReturnForPipe<R1> : Function,
|
|
148
|
+
R2 extends Plug<Files | undefined> ? (...args: A2) => PlugReturnForPipe<R2> : Function,
|
|
149
|
+
] :
|
|
150
|
+
T extends {
|
|
151
|
+
new (...args: infer A0): infer R0
|
|
152
|
+
new (...args: infer A1): infer R1
|
|
153
|
+
} ? [
|
|
154
|
+
R0 extends Plug<Files | undefined> ? (...args: A0) => PlugReturnForPipe<R0> : Function,
|
|
155
|
+
R1 extends Plug<Files | undefined> ? (...args: A1) => PlugReturnForPipe<R1> : Function,
|
|
156
|
+
] :
|
|
157
|
+
T extends {
|
|
158
|
+
new (...args: infer A0): infer R0
|
|
159
|
+
} ? [
|
|
160
|
+
R0 extends Plug<Files | undefined> ? (...args: A0) => PlugReturnForPipe<R0> : Function,
|
|
161
|
+
] :
|
|
162
|
+
never
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* A convenience type to easily annotate installed {@link Plug Plugs}.
|
|
166
|
+
*
|
|
167
|
+
* See also {@link install}.
|
|
168
|
+
*
|
|
169
|
+
* ```
|
|
170
|
+
* export class Write implements Plug {
|
|
171
|
+
* // ... the plug implementation lives here
|
|
172
|
+
* }
|
|
173
|
+
*
|
|
174
|
+
* install('write', Write)
|
|
175
|
+
*
|
|
176
|
+
* declare module '../pipe' {
|
|
177
|
+
* export interface Pipe {
|
|
178
|
+
* write: PipeExtension<typeof Write>
|
|
179
|
+
* }
|
|
180
|
+
* }
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
export type PipeExtension<T extends PlugConstructor, A = PlugConstructorOverloads<T>> =
|
|
184
|
+
A extends readonly [ infer First, ...infer Rest ] ?
|
|
185
|
+
First & PipeExtension<T, Rest> :
|
|
186
|
+
A extends readonly [ infer Only ] ?
|
|
187
|
+
Only :
|
|
188
|
+
Function
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Install a {@link Plug} into our {@link Pipe} prototype, and return a static
|
|
192
|
+
* creator function for the {@link Plug} itself.
|
|
193
|
+
*
|
|
194
|
+
* This allows our shorthand syntax for well-defined plugs such as:
|
|
195
|
+
*
|
|
196
|
+
* ```
|
|
197
|
+
* find('./src', '*.ts').write('./target')
|
|
198
|
+
* // Nicer and easier than...
|
|
199
|
+
* find('./src', '*.ts').plug(new Write('./target'))
|
|
200
|
+
* ```
|
|
201
|
+
*
|
|
202
|
+
* Use this alongside interface merging like:
|
|
203
|
+
*
|
|
204
|
+
* ```
|
|
205
|
+
* export class Write implements Plug {
|
|
206
|
+
* // ... the plug implementation lives here
|
|
207
|
+
* }
|
|
208
|
+
*
|
|
209
|
+
* install('write', Write)
|
|
210
|
+
*
|
|
211
|
+
* declare module '../pipe' {
|
|
212
|
+
* export interface Pipe {
|
|
213
|
+
* write: PipeExtension<typeof Write>
|
|
214
|
+
* }
|
|
215
|
+
* }
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
export function install<C extends PlugConstructor>(name: PlugName, ctor: C): void {
|
|
219
|
+
/* This is quite hairy when it comes to types, so, just give up! :-P */
|
|
220
|
+
|
|
221
|
+
function create(this: Pipe, ...args: any): Pipe & Promise<Files | undefined> {
|
|
222
|
+
// eslint-disable-next-line new-cap
|
|
223
|
+
return this.plug(new ctor(...args) as any)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/* Setup name so that stack traces look better */
|
|
227
|
+
Object.defineProperty(create, 'name', { value: name })
|
|
228
|
+
|
|
229
|
+
/* Inject the create function in the Pipe's prototype */
|
|
230
|
+
Object.defineProperty(Pipe.prototype, name, { value: create })
|
|
231
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import fs from '../utils/asyncfs.js'
|
|
2
|
+
|
|
3
|
+
import { assert } from '../assert.js'
|
|
4
|
+
import { Files } from '../files.js'
|
|
5
|
+
import { $p } from '../log.js'
|
|
6
|
+
import { assertAbsolutePath, getAbsoluteParent, resolveAbsolutePath } from '../paths.js'
|
|
7
|
+
import { install, Plug } from '../pipe.js'
|
|
8
|
+
import { Run } from '../run.js'
|
|
9
|
+
|
|
10
|
+
/** Options for copying files */
|
|
11
|
+
export interface CopyOptions {
|
|
12
|
+
/** Whether to allow overwriting or not (default `false`). */
|
|
13
|
+
overwrite?: boolean,
|
|
14
|
+
/** If specified, use this `mode` (octal string) when creating files. */
|
|
15
|
+
mode?: string | number,
|
|
16
|
+
/** If specified, use this `mode` (octal string) when creating directories. */
|
|
17
|
+
dirMode?: string | number,
|
|
18
|
+
/** If specified, this function will be invoked to rename files. */
|
|
19
|
+
rename?: (relative: string) => string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Copy the curent {@link Files} to a different directory */
|
|
23
|
+
export class Copy implements Plug<Files> {
|
|
24
|
+
constructor(directory: string, options?: CopyOptions)
|
|
25
|
+
constructor(
|
|
26
|
+
private readonly _directory: string,
|
|
27
|
+
private readonly _options: CopyOptions = {},
|
|
28
|
+
) {}
|
|
29
|
+
|
|
30
|
+
async pipe(files: Files, run: Run): Promise<Files> {
|
|
31
|
+
/* Destructure our options with some defaults and compute write flags */
|
|
32
|
+
const { mode, dirMode, overwrite, rename = (s): string => s } = this._options
|
|
33
|
+
const flags = overwrite ? fs.constants.COPYFILE_EXCL : 0
|
|
34
|
+
const dmode = parseMode(dirMode)
|
|
35
|
+
const fmode = parseMode(mode)
|
|
36
|
+
|
|
37
|
+
/* Our files builder for all written files */
|
|
38
|
+
const builder = run.files(this._directory)
|
|
39
|
+
|
|
40
|
+
/* Iterate through all the mappings of the source files */
|
|
41
|
+
for (const [ relative, absolute ] of files.pathMappings()) {
|
|
42
|
+
/* The target absolute is the (possibly) renamed relative source file
|
|
43
|
+
* relocated to the the target directory */
|
|
44
|
+
const target = resolveAbsolutePath(builder.directory, rename(relative))
|
|
45
|
+
|
|
46
|
+
/* We never copy a file onto itself, but not fail either */
|
|
47
|
+
if (target === absolute) {
|
|
48
|
+
run.log.warn('Cowardly refusing to copy same file', $p(absolute))
|
|
49
|
+
continue
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* Create the parent directory, recursively */
|
|
53
|
+
const directory = getAbsoluteParent(target)
|
|
54
|
+
const firstParent = await fs.mkdir(directory, { recursive: true })
|
|
55
|
+
|
|
56
|
+
/* Set the mode for all created directories */
|
|
57
|
+
if (firstParent && (dmode !== undefined)) {
|
|
58
|
+
assertAbsolutePath(firstParent)
|
|
59
|
+
for (let dir = directory; ; dir = getAbsoluteParent(dir)) {
|
|
60
|
+
run.log.trace(`Setting mode ${stringifyMode(dmode)} for directory`, $p(dir))
|
|
61
|
+
await fs.chmod(dir, dmode)
|
|
62
|
+
if (dir === firstParent) break
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* Actually _copy_ the file */
|
|
67
|
+
run.log.trace(`Copying "${$p(absolute)}" to "${$p(target)}"`)
|
|
68
|
+
await fs.copyFile(absolute, target, flags)
|
|
69
|
+
|
|
70
|
+
/* Set the mode, if we need to */
|
|
71
|
+
if (fmode !== undefined) {
|
|
72
|
+
run.log.trace(`Setting mode ${stringifyMode(fmode)} for file`, $p(target))
|
|
73
|
+
await fs.chmod(target, fmode)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* Record this file */
|
|
77
|
+
builder.add(relative)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const result = builder.build()
|
|
81
|
+
run.log.info('Copied', result.length, 'files to', $p(builder.directory))
|
|
82
|
+
return result
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* ========================================================================== *
|
|
87
|
+
* INSTALLATION *
|
|
88
|
+
* ========================================================================== */
|
|
89
|
+
|
|
90
|
+
install('copy', Copy)
|
|
91
|
+
|
|
92
|
+
declare module '../pipe.js' {
|
|
93
|
+
export interface Pipe {
|
|
94
|
+
/** Copy the curent {@link Files} to a different directory */
|
|
95
|
+
copy: PipeExtension<typeof Copy>
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* ========================================================================== *
|
|
100
|
+
* INTERNALS *
|
|
101
|
+
* ========================================================================== */
|
|
102
|
+
|
|
103
|
+
function parseMode(mode: string | number | undefined): number | undefined {
|
|
104
|
+
if (mode === undefined) return undefined
|
|
105
|
+
if (typeof mode === 'number') return mode
|
|
106
|
+
const parsed = parseInt(mode, 8)
|
|
107
|
+
assert(! isNaN(parsed), `Invalid mode "${mode}"`)
|
|
108
|
+
return parsed
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function stringifyMode(mode: number): string {
|
|
112
|
+
return mode.toString(8).padStart(4, '0')
|
|
113
|
+
}
|