@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.
Files changed (54) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +1062 -0
  3. package/bin/wireit.js +9 -0
  4. package/lib/analyzer.js +1600 -0
  5. package/lib/caching/cache.js +7 -0
  6. package/lib/caching/github-actions-cache.js +832 -0
  7. package/lib/caching/local-cache.js +78 -0
  8. package/lib/caching/shared-cache.js +256 -0
  9. package/lib/cli-options.js +495 -0
  10. package/lib/cli.js +177 -0
  11. package/lib/config.js +18 -0
  12. package/lib/error.js +160 -0
  13. package/lib/event.js +7 -0
  14. package/lib/execution/base.js +108 -0
  15. package/lib/execution/no-command.js +32 -0
  16. package/lib/execution/service.js +1017 -0
  17. package/lib/execution/standard.js +683 -0
  18. package/lib/executor.js +249 -0
  19. package/lib/fingerprint.js +164 -0
  20. package/lib/ide.js +583 -0
  21. package/lib/language-server.js +135 -0
  22. package/lib/logging/combination-logger.js +41 -0
  23. package/lib/logging/debug-logger.js +43 -0
  24. package/lib/logging/logger.js +38 -0
  25. package/lib/logging/metrics-logger.js +108 -0
  26. package/lib/logging/quiet/run-tracker.js +597 -0
  27. package/lib/logging/quiet/stack-map.js +41 -0
  28. package/lib/logging/quiet/writeover-line.js +197 -0
  29. package/lib/logging/quiet-logger.js +78 -0
  30. package/lib/logging/simple-logger.js +296 -0
  31. package/lib/logging/watch-logger.js +81 -0
  32. package/lib/script-child-process.js +270 -0
  33. package/lib/util/ast.js +71 -0
  34. package/lib/util/async-cache.js +24 -0
  35. package/lib/util/copy.js +120 -0
  36. package/lib/util/deferred.js +35 -0
  37. package/lib/util/delete.js +120 -0
  38. package/lib/util/dispose.js +16 -0
  39. package/lib/util/fs.js +258 -0
  40. package/lib/util/glob.js +255 -0
  41. package/lib/util/line-monitor.js +69 -0
  42. package/lib/util/manifest.js +31 -0
  43. package/lib/util/optimize-mkdirs.js +55 -0
  44. package/lib/util/package-json-reader.js +61 -0
  45. package/lib/util/package-json.js +179 -0
  46. package/lib/util/script-data-dir.js +19 -0
  47. package/lib/util/shuffle.js +16 -0
  48. package/lib/util/unreachable.js +12 -0
  49. package/lib/util/windows.js +87 -0
  50. package/lib/util/worker-pool.js +61 -0
  51. package/lib/watcher.js +396 -0
  52. package/package.json +470 -0
  53. package/schema.json +132 -0
  54. 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
@@ -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