@laurence79/wireit 0.14.13-shared-cache.0
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 +202 -0
- package/README.md +1062 -0
- package/bin/wireit.js +9 -0
- package/lib/analyzer.js +1600 -0
- package/lib/caching/cache.js +7 -0
- package/lib/caching/github-actions-cache.js +832 -0
- package/lib/caching/local-cache.js +78 -0
- package/lib/caching/shared-cache.js +256 -0
- package/lib/cli-options.js +495 -0
- package/lib/cli.js +177 -0
- package/lib/config.js +18 -0
- package/lib/error.js +160 -0
- package/lib/event.js +7 -0
- package/lib/execution/base.js +108 -0
- package/lib/execution/no-command.js +32 -0
- package/lib/execution/service.js +1017 -0
- package/lib/execution/standard.js +683 -0
- package/lib/executor.js +249 -0
- package/lib/fingerprint.js +164 -0
- package/lib/ide.js +583 -0
- package/lib/language-server.js +135 -0
- package/lib/logging/combination-logger.js +41 -0
- package/lib/logging/debug-logger.js +43 -0
- package/lib/logging/logger.js +38 -0
- package/lib/logging/metrics-logger.js +108 -0
- package/lib/logging/quiet/run-tracker.js +597 -0
- package/lib/logging/quiet/stack-map.js +41 -0
- package/lib/logging/quiet/writeover-line.js +197 -0
- package/lib/logging/quiet-logger.js +78 -0
- package/lib/logging/simple-logger.js +296 -0
- package/lib/logging/watch-logger.js +81 -0
- package/lib/script-child-process.js +270 -0
- package/lib/util/ast.js +71 -0
- package/lib/util/async-cache.js +24 -0
- package/lib/util/copy.js +120 -0
- package/lib/util/deferred.js +35 -0
- package/lib/util/delete.js +120 -0
- package/lib/util/dispose.js +16 -0
- package/lib/util/fs.js +258 -0
- package/lib/util/glob.js +255 -0
- package/lib/util/line-monitor.js +69 -0
- package/lib/util/manifest.js +31 -0
- package/lib/util/optimize-mkdirs.js +55 -0
- package/lib/util/package-json-reader.js +61 -0
- package/lib/util/package-json.js +179 -0
- package/lib/util/script-data-dir.js +19 -0
- package/lib/util/shuffle.js +16 -0
- package/lib/util/unreachable.js +12 -0
- package/lib/util/windows.js +87 -0
- package/lib/util/worker-pool.js +61 -0
- package/lib/watcher.js +396 -0
- package/package.json +470 -0
- package/schema.json +132 -0
- package/wireit.svg +1 -0
package/lib/util/fs.js
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2023 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
7
|
+
if (value !== null && value !== void 0) {
|
|
8
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
9
|
+
var dispose, inner;
|
|
10
|
+
if (async) {
|
|
11
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
12
|
+
dispose = value[Symbol.asyncDispose];
|
|
13
|
+
}
|
|
14
|
+
if (dispose === void 0) {
|
|
15
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
16
|
+
dispose = value[Symbol.dispose];
|
|
17
|
+
if (async) inner = dispose;
|
|
18
|
+
}
|
|
19
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
20
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
21
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
22
|
+
}
|
|
23
|
+
else if (async) {
|
|
24
|
+
env.stack.push({ async: true });
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
};
|
|
28
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
29
|
+
return function (env) {
|
|
30
|
+
function fail(e) {
|
|
31
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
32
|
+
env.hasError = true;
|
|
33
|
+
}
|
|
34
|
+
var r, s = 0;
|
|
35
|
+
function next() {
|
|
36
|
+
while (r = env.stack.pop()) {
|
|
37
|
+
try {
|
|
38
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
39
|
+
if (r.dispose) {
|
|
40
|
+
var result = r.dispose.call(r.value);
|
|
41
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
42
|
+
}
|
|
43
|
+
else s |= 1;
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
fail(e);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
50
|
+
if (env.hasError) throw env.error;
|
|
51
|
+
}
|
|
52
|
+
return next();
|
|
53
|
+
};
|
|
54
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
55
|
+
var e = new Error(message);
|
|
56
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
57
|
+
});
|
|
58
|
+
import * as fs from 'fs/promises';
|
|
59
|
+
import { createReadStream as rawCreateReadStream, createWriteStream as rawCreateWriteStream, } from 'fs';
|
|
60
|
+
import { Deferred } from './deferred.js';
|
|
61
|
+
import './dispose.js';
|
|
62
|
+
export { constants } from 'fs';
|
|
63
|
+
export class Semaphore {
|
|
64
|
+
#remaining;
|
|
65
|
+
#waiting = [];
|
|
66
|
+
constructor(numSlots) {
|
|
67
|
+
if (numSlots <= 0) {
|
|
68
|
+
throw new Error(`numSlots must be positive, got ${numSlots}`);
|
|
69
|
+
}
|
|
70
|
+
this.#remaining = numSlots;
|
|
71
|
+
}
|
|
72
|
+
async reserve() {
|
|
73
|
+
while (this.#remaining === 0) {
|
|
74
|
+
const deferred = new Deferred();
|
|
75
|
+
this.#waiting.push(deferred);
|
|
76
|
+
await deferred.promise;
|
|
77
|
+
}
|
|
78
|
+
this.#remaining--;
|
|
79
|
+
let disposed = false;
|
|
80
|
+
return {
|
|
81
|
+
[Symbol.dispose]: () => {
|
|
82
|
+
if (disposed) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
disposed = true;
|
|
86
|
+
this.#remaining++;
|
|
87
|
+
if (this.#waiting.length > 0) {
|
|
88
|
+
this.#waiting.pop()?.resolve();
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export const fileBudget = (() => {
|
|
95
|
+
let maxOpenFiles = Number(process.env['WIREIT_MAX_OPEN_FILES']);
|
|
96
|
+
if (isNaN(maxOpenFiles)) {
|
|
97
|
+
// This is tricky to get right. There's no simple cross-platform way to
|
|
98
|
+
// determine what our current limits are. Windows it's 512, on macOS it
|
|
99
|
+
// defaults to 256, and on Linux it varies a lot.
|
|
100
|
+
// 200 gives us a bit of headroom for other things that might be using
|
|
101
|
+
// file descriptors in our process, like node internals.
|
|
102
|
+
maxOpenFiles = 200;
|
|
103
|
+
}
|
|
104
|
+
return new Semaphore(maxOpenFiles);
|
|
105
|
+
})();
|
|
106
|
+
export async function mkdir(path, options) {
|
|
107
|
+
const reservation = await fileBudget.reserve();
|
|
108
|
+
try {
|
|
109
|
+
return await fs.mkdir(path, options);
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
reservation[Symbol.dispose]();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
export async function mkdtemp(path) {
|
|
116
|
+
const reservation = await fileBudget.reserve();
|
|
117
|
+
try {
|
|
118
|
+
return await fs.mkdtemp(path);
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
reservation[Symbol.dispose]();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export async function writeFile(path, contents, encoding) {
|
|
125
|
+
const reservation = await fileBudget.reserve();
|
|
126
|
+
try {
|
|
127
|
+
return await fs.writeFile(path, contents, encoding);
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
reservation[Symbol.dispose]();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
export async function readFile(path, encoding) {
|
|
134
|
+
const reservation = await fileBudget.reserve();
|
|
135
|
+
try {
|
|
136
|
+
return await fs.readFile(path, encoding);
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
reservation[Symbol.dispose]();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
export async function rm(path, options) {
|
|
143
|
+
const reservation = await fileBudget.reserve();
|
|
144
|
+
try {
|
|
145
|
+
return await fs.rm(path, options);
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
reservation[Symbol.dispose]();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
export async function lstat(path) {
|
|
152
|
+
const reservation = await fileBudget.reserve();
|
|
153
|
+
try {
|
|
154
|
+
return await fs.lstat(path);
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
reservation[Symbol.dispose]();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
export async function stat(path) {
|
|
161
|
+
const reservation = await fileBudget.reserve();
|
|
162
|
+
try {
|
|
163
|
+
return await fs.stat(path);
|
|
164
|
+
}
|
|
165
|
+
finally {
|
|
166
|
+
reservation[Symbol.dispose]();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
export async function access(path) {
|
|
170
|
+
const reservation = await fileBudget.reserve();
|
|
171
|
+
try {
|
|
172
|
+
return await fs.access(path);
|
|
173
|
+
}
|
|
174
|
+
finally {
|
|
175
|
+
reservation[Symbol.dispose]();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
export async function createReadStream(path, options) {
|
|
179
|
+
const reservation = await fileBudget.reserve();
|
|
180
|
+
const stream = rawCreateReadStream(path, options);
|
|
181
|
+
stream.on('close', () => reservation[Symbol.dispose]());
|
|
182
|
+
return stream;
|
|
183
|
+
}
|
|
184
|
+
export async function createWriteStream(path) {
|
|
185
|
+
const reservation = await fileBudget.reserve();
|
|
186
|
+
const stream = rawCreateWriteStream(path);
|
|
187
|
+
stream.on('close', () => reservation[Symbol.dispose]());
|
|
188
|
+
return stream;
|
|
189
|
+
}
|
|
190
|
+
export async function copyFile(src, dest, flags) {
|
|
191
|
+
const reservation = await fileBudget.reserve();
|
|
192
|
+
try {
|
|
193
|
+
return await fs.copyFile(src, dest, flags);
|
|
194
|
+
}
|
|
195
|
+
finally {
|
|
196
|
+
reservation[Symbol.dispose]();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
export async function readlink(path, options) {
|
|
200
|
+
const reservation = await fileBudget.reserve();
|
|
201
|
+
try {
|
|
202
|
+
return await fs.readlink(path, options);
|
|
203
|
+
}
|
|
204
|
+
finally {
|
|
205
|
+
reservation[Symbol.dispose]();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
export async function symlink(target, path, type) {
|
|
209
|
+
const reservation = await fileBudget.reserve();
|
|
210
|
+
try {
|
|
211
|
+
return await fs.symlink(target, path, type);
|
|
212
|
+
}
|
|
213
|
+
finally {
|
|
214
|
+
reservation[Symbol.dispose]();
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
export async function unlink(target) {
|
|
218
|
+
const reservation = await fileBudget.reserve();
|
|
219
|
+
try {
|
|
220
|
+
return await fs.unlink(target);
|
|
221
|
+
}
|
|
222
|
+
finally {
|
|
223
|
+
reservation[Symbol.dispose]();
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
export async function rename(oldPath, newPath) {
|
|
227
|
+
const reservation = await fileBudget.reserve();
|
|
228
|
+
try {
|
|
229
|
+
return await fs.rename(oldPath, newPath);
|
|
230
|
+
}
|
|
231
|
+
finally {
|
|
232
|
+
reservation[Symbol.dispose]();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
export async function rmdir(target) {
|
|
236
|
+
const reservation = await fileBudget.reserve();
|
|
237
|
+
try {
|
|
238
|
+
return await fs.rmdir(target);
|
|
239
|
+
}
|
|
240
|
+
finally {
|
|
241
|
+
reservation[Symbol.dispose]();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
export async function readdir(path, options) {
|
|
245
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
246
|
+
try {
|
|
247
|
+
const _reservation = __addDisposableResource(env_1, await fileBudget.reserve(), false);
|
|
248
|
+
return await fs.readdir(path, options);
|
|
249
|
+
}
|
|
250
|
+
catch (e_1) {
|
|
251
|
+
env_1.error = e_1;
|
|
252
|
+
env_1.hasError = true;
|
|
253
|
+
}
|
|
254
|
+
finally {
|
|
255
|
+
__disposeResources(env_1);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
//# sourceMappingURL=fs.js.map
|
package/lib/util/glob.js
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2022 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import fastGlob from 'fast-glob';
|
|
7
|
+
import { expand as braces } from 'brace-expansion';
|
|
8
|
+
import * as pathlib from 'path';
|
|
9
|
+
/**
|
|
10
|
+
* The error raised when {@link glob} matches a path that is outside of
|
|
11
|
+
* {@link GlobOptions.cwd} when {@link GlobOptions.throwIfOutsideCwd} is `true`.
|
|
12
|
+
*/
|
|
13
|
+
export class GlobOutsideCwdError extends Error {
|
|
14
|
+
constructor(path, cwd) {
|
|
15
|
+
super(`${JSON.stringify(path)} was outside ${JSON.stringify(cwd)}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Match glob patterns against the file system.
|
|
20
|
+
*
|
|
21
|
+
* - Input patterns must be / separated.
|
|
22
|
+
* - Matches are returned with the OS-specific separator.
|
|
23
|
+
* - Leading `/`s are interpreted as relative to the `cwd`, instead of the root
|
|
24
|
+
* of the filesystem.
|
|
25
|
+
* - Dot (aka hidden) files are always matched.
|
|
26
|
+
* - Empty or blank patterns throw.
|
|
27
|
+
* - The order of "!exclusion" patterns matter (i.e. files can be "re-included"
|
|
28
|
+
* after exclusion).
|
|
29
|
+
* - Results are always absolute.
|
|
30
|
+
*
|
|
31
|
+
* @param patterns The glob patterns to match. Must use forward-slash separator,
|
|
32
|
+
* even on Windows.
|
|
33
|
+
* @params opts See {@link GlobOptions}.
|
|
34
|
+
*/
|
|
35
|
+
export async function glob(patterns, opts) {
|
|
36
|
+
if (patterns.length === 0) {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
const expandedPatterns = []; // New array so we don't mutate input patterns array.
|
|
40
|
+
for (const pattern of patterns) {
|
|
41
|
+
// We need to expand `{foo,bar}` style brace patterns ourselves so that we
|
|
42
|
+
// can reliably interpret the syntax of the pattern. For example, for
|
|
43
|
+
// re-rooting we need to check for a leading `/`, but we can't do that
|
|
44
|
+
// directly on `{/foo,/bar}`.
|
|
45
|
+
const allExpanded = pattern === '' ? [''] : braces(pattern);
|
|
46
|
+
for (const expanded of allExpanded) {
|
|
47
|
+
expandedPatterns.push(expanded);
|
|
48
|
+
if (opts.expandDirectories) {
|
|
49
|
+
// Also include a recursive-children version of every pattern, in case
|
|
50
|
+
// the pattern refers to a directory. This gives us behavior similar to
|
|
51
|
+
// the npm package.json "files" array, where matching a directory
|
|
52
|
+
// implicitly includes all transitive children.
|
|
53
|
+
if (!isRecursive(expanded)) {
|
|
54
|
+
const isExclusive = pattern[0] === '!';
|
|
55
|
+
if (!isExclusive) {
|
|
56
|
+
// We use the "ignore" feature of fast-glob for exclusive patterns,
|
|
57
|
+
// which already automatically recursively excludes directories by
|
|
58
|
+
// not recursing into them at all, so there is no need to also
|
|
59
|
+
// generate a recursive version when excluding.
|
|
60
|
+
expandedPatterns.push(expanded + (expanded.endsWith('/') ? '**' : '/**'));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// fast-glob doesn't pay attention to the order of !excluded patterns. For
|
|
67
|
+
// example, the following pattern array should include "foo/bar/baz", but
|
|
68
|
+
// fast-glob excludes it:
|
|
69
|
+
//
|
|
70
|
+
// foo/**
|
|
71
|
+
// !foo/bar/**
|
|
72
|
+
// foo/bar/baz <-- wrongly excluded
|
|
73
|
+
// !foo/qux
|
|
74
|
+
//
|
|
75
|
+
// To fix this behavior, we divide the patterns into groups that can be
|
|
76
|
+
// evaluated separately and then combined. We create a new group whenever an
|
|
77
|
+
// !exclude pattern is in front of an include pattern, because that's when
|
|
78
|
+
// this problem could occur, and we include all subsequent negations (but not
|
|
79
|
+
// preceding ones) in each group:
|
|
80
|
+
//
|
|
81
|
+
// Group 1:
|
|
82
|
+
// include: foo/**
|
|
83
|
+
// exclude: foo/bar/**
|
|
84
|
+
// exclude: foo/qux
|
|
85
|
+
//
|
|
86
|
+
// Group 2:
|
|
87
|
+
// include: foo/bar/baz
|
|
88
|
+
// exclude: foo/qux
|
|
89
|
+
let currentGroup = { include: [], exclude: [] };
|
|
90
|
+
const groups = [currentGroup];
|
|
91
|
+
let prevWasInclusive = false;
|
|
92
|
+
// We want each group to include all subsequent negated patterns. The simplest
|
|
93
|
+
// way to do that is to build the groups backwards.
|
|
94
|
+
for (let i = expandedPatterns.length - 1; i >= 0; i--) {
|
|
95
|
+
let pattern = expandedPatterns[i];
|
|
96
|
+
const isExclusive = pattern[0] === '!';
|
|
97
|
+
if (isExclusive) {
|
|
98
|
+
pattern = pattern.slice(1); // Remove the "!"
|
|
99
|
+
}
|
|
100
|
+
// Ignore leading `/`s so that e.g. "/foo" is interpreted relative to the
|
|
101
|
+
// cwd, instead of relative to the root of the filesystem. We want to include
|
|
102
|
+
// >1 leading slashes, since those are technically valid paths too.
|
|
103
|
+
pattern = pattern.replace(/^\/+/, '');
|
|
104
|
+
if (isExclusive) {
|
|
105
|
+
if (prevWasInclusive) {
|
|
106
|
+
// A new group is needed because this exclusion comes before an
|
|
107
|
+
// inclusion.
|
|
108
|
+
//
|
|
109
|
+
// foo/**
|
|
110
|
+
// !foo/bar/** <-- we are here
|
|
111
|
+
// foo/bar/baz <-- this is the previous one
|
|
112
|
+
// !foo/qux
|
|
113
|
+
currentGroup = { include: [], exclude: [] };
|
|
114
|
+
for (const previousGroup of groups) {
|
|
115
|
+
// Also include all exclusions we've accumulated so far into the new
|
|
116
|
+
// group (since we're iterating backwards, these are the exclusions
|
|
117
|
+
// that come after it).
|
|
118
|
+
currentGroup.exclude.push(...previousGroup.exclude);
|
|
119
|
+
}
|
|
120
|
+
groups.push(currentGroup);
|
|
121
|
+
}
|
|
122
|
+
currentGroup.exclude.push(
|
|
123
|
+
// Trim trailing slashes because fast-glob does not understand trailing
|
|
124
|
+
// slashes in "ignore" list entries (they have no effect!).
|
|
125
|
+
pattern.replace(/\/+$/, ''));
|
|
126
|
+
}
|
|
127
|
+
else if (pattern.match(/^\s*$/)) {
|
|
128
|
+
// fast-glob already throws on empty strings, but we also throw on
|
|
129
|
+
// only-whitespace patterns.
|
|
130
|
+
//
|
|
131
|
+
// Note minor optimization here: there is no reason to check this regexp
|
|
132
|
+
// on exclusive patterns, because by definition they start with a "!" so
|
|
133
|
+
// can't have been empty/blank.
|
|
134
|
+
throw new Error(`glob encountered empty or blank pattern: ${JSON.stringify(pattern)}`);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
currentGroup.include.push(pattern);
|
|
138
|
+
}
|
|
139
|
+
prevWasInclusive = !isExclusive;
|
|
140
|
+
}
|
|
141
|
+
// Pass each group to fast-glob to match in parallel, and combine into a
|
|
142
|
+
// single set.
|
|
143
|
+
const combinedMap = new Map();
|
|
144
|
+
// Ensure the cwd is absolute and normalized so that we can do path string
|
|
145
|
+
// comparisons.
|
|
146
|
+
const normalizedCwd = pathlib.resolve(opts.cwd);
|
|
147
|
+
const normalizedCwdWithTrailingSep = normalizedCwd + pathlib.sep;
|
|
148
|
+
await Promise.all(groups.map(async ({ include, exclude }) => {
|
|
149
|
+
const matches = await fastGlob(include, {
|
|
150
|
+
ignore: exclude,
|
|
151
|
+
cwd: normalizedCwd,
|
|
152
|
+
dot: true,
|
|
153
|
+
onlyFiles: !opts.includeDirectories,
|
|
154
|
+
absolute: true,
|
|
155
|
+
followSymbolicLinks: opts.followSymlinks,
|
|
156
|
+
// This should have no overhead because fast-glob already uses these
|
|
157
|
+
// objects for its internal representation:
|
|
158
|
+
// https://github.com/mrmlnc/fast-glob#objectmode
|
|
159
|
+
objectMode: true,
|
|
160
|
+
// Since we append "/**" to patterns above, we will sometimes get
|
|
161
|
+
// ENOTDIR errors when the path we appended to was not a directory. We
|
|
162
|
+
// can't know in advance which patterns refer to directories.
|
|
163
|
+
suppressErrors: true,
|
|
164
|
+
// We already do brace expansion ourselves. Doing it again would be
|
|
165
|
+
// inefficient and would also break brace escaping.
|
|
166
|
+
braceExpansion: false,
|
|
167
|
+
});
|
|
168
|
+
const potentiallyProblematicSymlinkParents = new Set();
|
|
169
|
+
for (const match of matches) {
|
|
170
|
+
// Normalize the path so that:
|
|
171
|
+
//
|
|
172
|
+
// 1. We have native path separators. fast-glob returns "/" even on
|
|
173
|
+
// Windows.
|
|
174
|
+
//
|
|
175
|
+
// 2. Remnants of input pattern syntax like ".." and trailing "/"s are
|
|
176
|
+
// removed (which fast-glob preserves in the results). Note that
|
|
177
|
+
// `fs.normalize` does not trim trailing "/"s, so we do that
|
|
178
|
+
// ourselves (`fs.resolve` does, but that also makes the path
|
|
179
|
+
// absolute).
|
|
180
|
+
match.path = pathlib.normalize(match.path.replace(/\/+$/g, ''));
|
|
181
|
+
if (opts.throwIfOutsideCwd) {
|
|
182
|
+
const absPath = match.path;
|
|
183
|
+
if (
|
|
184
|
+
// Match "parent/child" and "parent", but not "parentx".
|
|
185
|
+
!absPath.startsWith(normalizedCwdWithTrailingSep) &&
|
|
186
|
+
absPath !== normalizedCwd) {
|
|
187
|
+
// TODO(aomarks) This check could in theory be done before we execute
|
|
188
|
+
// the globs, but we'd need to be really sure we account for special
|
|
189
|
+
// glob syntax, which could make it not 100% straightforward to do
|
|
190
|
+
// path checking. Checking the resulting paths is straightforward
|
|
191
|
+
// because we know they don't contain special syntax.
|
|
192
|
+
throw new GlobOutsideCwdError(absPath, opts.cwd);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
combinedMap.set(match.path, match);
|
|
196
|
+
if (opts.expandDirectories &&
|
|
197
|
+
!opts.followSymlinks &&
|
|
198
|
+
match.dirent.isSymbolicLink()) {
|
|
199
|
+
potentiallyProblematicSymlinkParents.add(match.path);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (potentiallyProblematicSymlinkParents.size > 0) {
|
|
203
|
+
// When the user passes a path "foo" and expandDirectories is true, we
|
|
204
|
+
// convert the pattern to "foo/**" (see above about expandDirectories
|
|
205
|
+
// for why).
|
|
206
|
+
//
|
|
207
|
+
// However, what if "foo" is a symlink to a folder with some children,
|
|
208
|
+
// and followSymbolicLinks is false (which is the combination of
|
|
209
|
+
// settings we use when wireit globs output files for caching)?
|
|
210
|
+
//
|
|
211
|
+
// Well, if you pass "foo/**" to fast-glob where "foo" is a symlink to a
|
|
212
|
+
// directory, it's actually going to follow the symlink and return its
|
|
213
|
+
// children, even if followSymbolicLinks is false. (This seems fairly
|
|
214
|
+
// reasonable, since otherwise it would always have to check all parent
|
|
215
|
+
// directories of a path before reading any directory contents in case
|
|
216
|
+
// there's a symlink somewhere up the tree).
|
|
217
|
+
//
|
|
218
|
+
// But that's bad for us, because if a wireit user has a script that
|
|
219
|
+
// creates a symlink to a directory, and they list that symlink directly
|
|
220
|
+
// in their output paths, then for the purposes of caching we really
|
|
221
|
+
// just want to just restore the literal symlink, and not copy its
|
|
222
|
+
// contents. (Otherwise it would be a symlink when built the first time,
|
|
223
|
+
// but a regular folder with children when restored from cache).
|
|
224
|
+
//
|
|
225
|
+
// So, since we don't know whether we might have problematically
|
|
226
|
+
// appended a "/**" to a symlink, we will instead filter out child
|
|
227
|
+
// matches. (Otherwise we'd need to check every given path to see if
|
|
228
|
+
// it's a symlink, and directly listed symlinks are pretty rare, this
|
|
229
|
+
// post-hoc approach is probably more efficient on average).
|
|
230
|
+
for (const match of combinedMap.values()) {
|
|
231
|
+
// Walk up the file hierarchy to check if any parent was a symlink
|
|
232
|
+
// that was also directly matched by the glob.
|
|
233
|
+
let child = match.path;
|
|
234
|
+
while (true) {
|
|
235
|
+
const parent = pathlib.dirname(child);
|
|
236
|
+
if (parent === child) {
|
|
237
|
+
// Reached the filesystem root.
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
if (potentiallyProblematicSymlinkParents.has(parent)) {
|
|
241
|
+
combinedMap.delete(match.path);
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
child = parent;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}));
|
|
249
|
+
return [...combinedMap.values()];
|
|
250
|
+
}
|
|
251
|
+
const isRecursive = (pattern) => pattern === '**' ||
|
|
252
|
+
pattern === '**/*' ||
|
|
253
|
+
pattern.endsWith('/**') ||
|
|
254
|
+
pattern.endsWith('/**/*');
|
|
255
|
+
//# sourceMappingURL=glob.js.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2022 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { Deferred } from './deferred.js';
|
|
7
|
+
/**
|
|
8
|
+
* Monitors the stdout and stderr of a child process line-by-line searching for
|
|
9
|
+
* a match of the given regular expression.
|
|
10
|
+
*
|
|
11
|
+
* Note we can't use readline here because we want to check lines that haven't
|
|
12
|
+
* completed yet.
|
|
13
|
+
*/
|
|
14
|
+
export class LineMonitor {
|
|
15
|
+
#child;
|
|
16
|
+
#pattern;
|
|
17
|
+
#matched;
|
|
18
|
+
#stdout;
|
|
19
|
+
#stderr;
|
|
20
|
+
constructor(child, pattern) {
|
|
21
|
+
this.#matched = new Deferred();
|
|
22
|
+
this.#stdout = '';
|
|
23
|
+
this.#stderr = '';
|
|
24
|
+
/**
|
|
25
|
+
* Resolves to `{"ok": true}` when a match was found or `{"ok": false}` when
|
|
26
|
+
* this monitor was aborted.
|
|
27
|
+
*/
|
|
28
|
+
this.matched = this.#matched.promise;
|
|
29
|
+
this.#onStdout = (data) => {
|
|
30
|
+
this.#stdout = this.#check(this.#stdout + String(data));
|
|
31
|
+
};
|
|
32
|
+
this.#onStderr = (data) => {
|
|
33
|
+
this.#stderr = this.#check(this.#stderr + String(data));
|
|
34
|
+
};
|
|
35
|
+
this.#child = child;
|
|
36
|
+
this.#pattern = pattern;
|
|
37
|
+
child.stdout.on('data', this.#onStdout);
|
|
38
|
+
child.stderr.on('data', this.#onStderr);
|
|
39
|
+
}
|
|
40
|
+
abort() {
|
|
41
|
+
this.#removeEventListeners();
|
|
42
|
+
this.#matched.resolve({ ok: false, error: undefined });
|
|
43
|
+
}
|
|
44
|
+
#removeEventListeners() {
|
|
45
|
+
this.#child.stdout.removeListener('data', this.#onStdout);
|
|
46
|
+
this.#child.stderr.removeListener('data', this.#onStderr);
|
|
47
|
+
}
|
|
48
|
+
#onStdout;
|
|
49
|
+
#onStderr;
|
|
50
|
+
#check(buffer) {
|
|
51
|
+
const lines = buffer.split(/\n/g);
|
|
52
|
+
let end = 0;
|
|
53
|
+
for (let i = 0; i < lines.length; i++) {
|
|
54
|
+
const line = lines[i];
|
|
55
|
+
if (i !== lines.length - 1) {
|
|
56
|
+
// Don't move beyond the final line, since it might be incomplete, and
|
|
57
|
+
// we want to match the entire line the next time _check is called.
|
|
58
|
+
end += line.length + 1;
|
|
59
|
+
}
|
|
60
|
+
if (this.#pattern.test(line)) {
|
|
61
|
+
this.#removeEventListeners();
|
|
62
|
+
this.#matched.resolve({ ok: true, value: undefined });
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return buffer.slice(end);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=line-monitor.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2022 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
export function computeManifestEntry(stats) {
|
|
7
|
+
return {
|
|
8
|
+
t: stats.isFile()
|
|
9
|
+
? 'f'
|
|
10
|
+
: stats.isDirectory()
|
|
11
|
+
? 'd'
|
|
12
|
+
: stats.isSymbolicLink()
|
|
13
|
+
? 'l'
|
|
14
|
+
: stats.isBlockDevice()
|
|
15
|
+
? 'b'
|
|
16
|
+
: stats.isCharacterDevice()
|
|
17
|
+
? 'c'
|
|
18
|
+
: stats.isFIFO()
|
|
19
|
+
? 'p'
|
|
20
|
+
: stats.isSocket()
|
|
21
|
+
? 's'
|
|
22
|
+
: '?',
|
|
23
|
+
// Don't include timestamp or size for directories, because they can change
|
|
24
|
+
// when a child is added or removed. If we are tracking the child, then it
|
|
25
|
+
// will have its own entry. If we are not tracking the child, then we don't
|
|
26
|
+
// want it to affect the manifest.
|
|
27
|
+
m: stats.isDirectory() ? undefined : stats.mtimeMs,
|
|
28
|
+
s: stats.isDirectory() ? undefined : stats.size,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2022 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { dirname } from 'path';
|
|
7
|
+
/**
|
|
8
|
+
* Given a set of filesystem directory paths, returns the smallest set of
|
|
9
|
+
* recursive {@link fs.mkdir} operations required to create all directories.
|
|
10
|
+
*
|
|
11
|
+
* For example, given:
|
|
12
|
+
*
|
|
13
|
+
* a/b/c
|
|
14
|
+
* a/b
|
|
15
|
+
* d
|
|
16
|
+
* d/e/f/g/h
|
|
17
|
+
*
|
|
18
|
+
* Returns:
|
|
19
|
+
*
|
|
20
|
+
* a/b/c
|
|
21
|
+
* d/e/f/g/h
|
|
22
|
+
*
|
|
23
|
+
* Note this function does an in-place sort of the given dirs.
|
|
24
|
+
*/
|
|
25
|
+
export const optimizeMkdirs = (dirs) => {
|
|
26
|
+
if (dirs.length <= 1) {
|
|
27
|
+
return dirs;
|
|
28
|
+
}
|
|
29
|
+
const ops = [];
|
|
30
|
+
// Sorting from longest to shortest ensures that child directories come before
|
|
31
|
+
// parents (e.g. [d/e, d/e/f, a, a/b/c] => [d/e/f, a/b/c, d/e, a]).
|
|
32
|
+
// Parent/child adjacency doesn't matter.
|
|
33
|
+
dirs.sort((a, b) => b.length - a.length);
|
|
34
|
+
const handled = new Set();
|
|
35
|
+
for (const dir of dirs) {
|
|
36
|
+
if (handled.has(dir)) {
|
|
37
|
+
// Skip this directory because it has already been handled by a longer
|
|
38
|
+
// path we've already seen (e.g. "a/b/c" also creates "a/b" and "a").
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
ops.push(dir);
|
|
42
|
+
// Add this directory and all of its parent directories to the "done" set.
|
|
43
|
+
let cur = dir;
|
|
44
|
+
while (true) {
|
|
45
|
+
handled.add(cur);
|
|
46
|
+
const parent = dirname(cur);
|
|
47
|
+
if (parent === cur) {
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
cur = parent;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return ops;
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=optimize-mkdirs.js.map
|