@limrun/api 0.22.2 → 0.23.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/CHANGELOG.md +8 -0
- package/exec-client.d.mts +5 -1
- package/exec-client.d.mts.map +1 -1
- package/exec-client.d.ts +5 -1
- package/exec-client.d.ts.map +1 -1
- package/exec-client.js +14 -7
- package/exec-client.js.map +1 -1
- package/exec-client.mjs +14 -7
- package/exec-client.mjs.map +1 -1
- package/folder-sync-ignore.d.mts +7 -0
- package/folder-sync-ignore.d.mts.map +1 -0
- package/folder-sync-ignore.d.ts +7 -0
- package/folder-sync-ignore.d.ts.map +1 -0
- package/folder-sync-ignore.js +56 -0
- package/folder-sync-ignore.js.map +1 -0
- package/folder-sync-ignore.mjs +52 -0
- package/folder-sync-ignore.mjs.map +1 -0
- package/folder-sync-watcher.d.mts +2 -0
- package/folder-sync-watcher.d.mts.map +1 -1
- package/folder-sync-watcher.d.ts +2 -0
- package/folder-sync-watcher.d.ts.map +1 -1
- package/folder-sync-watcher.js +10 -62
- package/folder-sync-watcher.js.map +1 -1
- package/folder-sync-watcher.mjs +10 -62
- package/folder-sync-watcher.mjs.map +1 -1
- package/folder-sync.d.mts +16 -16
- package/folder-sync.d.mts.map +1 -1
- package/folder-sync.d.ts +16 -16
- package/folder-sync.d.ts.map +1 -1
- package/folder-sync.js +22 -65
- package/folder-sync.js.map +1 -1
- package/folder-sync.mjs +21 -64
- package/folder-sync.mjs.map +1 -1
- package/ios-client.d.mts +24 -0
- package/ios-client.d.mts.map +1 -1
- package/ios-client.d.ts +24 -0
- package/ios-client.d.ts.map +1 -1
- package/ios-client.js +108 -8
- package/ios-client.js.map +1 -1
- package/ios-client.mjs +109 -9
- package/ios-client.mjs.map +1 -1
- package/package.json +11 -1
- package/sandbox-client.d.mts +20 -15
- package/sandbox-client.d.mts.map +1 -1
- package/sandbox-client.d.ts +20 -15
- package/sandbox-client.d.ts.map +1 -1
- package/sandbox-client.js +49 -40
- package/sandbox-client.js.map +1 -1
- package/sandbox-client.mjs +48 -40
- package/sandbox-client.mjs.map +1 -1
- package/src/exec-client.ts +14 -7
- package/src/folder-sync-ignore.ts +65 -0
- package/src/folder-sync-watcher.ts +11 -66
- package/src/folder-sync.ts +39 -89
- package/src/ios-client.ts +138 -9
- package/src/sandbox-client.ts +72 -62
- package/src/version.ts +1 -1
- package/version.d.mts +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.mjs +1 -1
package/sandbox-client.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createXCodeSandboxClient = createXCodeSandboxClient;
|
|
4
|
+
const tslib_1 = require("./internal/tslib.js");
|
|
5
|
+
const os_1 = tslib_1.__importDefault(require("os"));
|
|
6
|
+
const path_1 = tslib_1.__importDefault(require("path"));
|
|
4
7
|
const folder_sync_1 = require("./folder-sync.js");
|
|
5
8
|
const exec_client_1 = require("./exec-client.js");
|
|
9
|
+
const folder_sync_ignore_1 = require("./folder-sync-ignore.js");
|
|
10
|
+
const crypto_1 = tslib_1.__importDefault(require("crypto"));
|
|
6
11
|
/**
|
|
7
12
|
* Creates a client for interacting with a sandboxed Xcode build service.
|
|
8
13
|
*
|
|
@@ -49,7 +54,7 @@ async function createXCodeSandboxClient(options) {
|
|
|
49
54
|
console.error('[XCodeSandbox]', ...args);
|
|
50
55
|
},
|
|
51
56
|
};
|
|
52
|
-
const
|
|
57
|
+
const log = (level, msg) => {
|
|
53
58
|
switch (level) {
|
|
54
59
|
case 'debug':
|
|
55
60
|
logger.debug(msg);
|
|
@@ -89,49 +94,53 @@ async function createXCodeSandboxClient(options) {
|
|
|
89
94
|
}
|
|
90
95
|
return {
|
|
91
96
|
async sync(localCodePath, opts) {
|
|
97
|
+
// Use folder name and hash of absolute path to scope basisCacheDir uniquely for each sync root
|
|
98
|
+
const resolvedPath = path_1.default.resolve(localCodePath);
|
|
99
|
+
const folderName = path_1.default.basename(resolvedPath);
|
|
100
|
+
const hash = crypto_1.default.createHash('sha1').update(resolvedPath).digest('hex').slice(0, 8);
|
|
101
|
+
const cacheKey = `limsync-cache-${folderName}-${hash}`;
|
|
102
|
+
const basisCacheDir = opts?.basisCacheDir ?? path_1.default.join(os_1.default.tmpdir(), cacheKey);
|
|
92
103
|
const codeSyncOpts = {
|
|
93
104
|
apiUrl: options.apiUrl,
|
|
94
105
|
token: options.token,
|
|
95
|
-
udid:
|
|
96
|
-
install:
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
relativePath.startsWith('
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
106
|
+
udid: cacheKey,
|
|
107
|
+
install: opts?.install ?? true,
|
|
108
|
+
ignoreFn: await (0, folder_sync_ignore_1.createIgnoreFn)(localCodePath, {
|
|
109
|
+
basisCacheDir,
|
|
110
|
+
additional: (relativePath) => {
|
|
111
|
+
if (relativePath.startsWith('build/') ||
|
|
112
|
+
relativePath.startsWith('.build/') ||
|
|
113
|
+
relativePath.startsWith('DerivedData/') ||
|
|
114
|
+
relativePath.startsWith('Index.noindex/') ||
|
|
115
|
+
relativePath.startsWith('ModuleCache.noindex/') ||
|
|
116
|
+
relativePath.startsWith('.index-build/')) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
if (relativePath.startsWith('.swiftpm/') ||
|
|
120
|
+
relativePath.startsWith('Pods/') ||
|
|
121
|
+
relativePath.startsWith('Carthage/Build/')) {
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
if (relativePath.includes('/xcuserdata/')) {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
if (relativePath.includes('.dSYM/')) {
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
// User-provided ignores
|
|
131
|
+
if (opts?.ignore?.(relativePath)) {
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
104
134
|
return false;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
relativePath.startsWith('.limsync-cache/') ||
|
|
113
|
-
relativePath === '.DS_Store' ||
|
|
114
|
-
relativePath.endsWith('/.DS_Store')) {
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
if (relativePath.includes('/xcuserdata/')) {
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
if (relativePath.includes('.dSYM/')) {
|
|
121
|
-
return false;
|
|
122
|
-
}
|
|
123
|
-
// User-provided filter
|
|
124
|
-
if (opts?.filter && !opts.filter(relativePath)) {
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
return true;
|
|
128
|
-
},
|
|
129
|
-
...(opts?.basisCacheDir ? { basisCacheDir: opts.basisCacheDir } : {}),
|
|
130
|
-
...(opts?.maxPatchBytes !== undefined ? { maxPatchBytes: opts.maxPatchBytes } : {}),
|
|
131
|
-
...(opts?.watch !== undefined ? { watch: opts.watch } : {}),
|
|
132
|
-
log: opts?.log ?? logFn,
|
|
135
|
+
},
|
|
136
|
+
}),
|
|
137
|
+
basisCacheDir,
|
|
138
|
+
watch: opts?.watch ?? true,
|
|
139
|
+
maxPatchBytes: opts?.maxPatchBytes ?? 4 * 1024 * 1024,
|
|
140
|
+
launchMode: 'ForegroundIfRunning',
|
|
141
|
+
log,
|
|
133
142
|
};
|
|
134
|
-
const result = await (0, folder_sync_1.
|
|
143
|
+
const result = await (0, folder_sync_1.syncFolder)(localCodePath, codeSyncOpts);
|
|
135
144
|
if (result.stopWatching) {
|
|
136
145
|
return { stopWatching: result.stopWatching };
|
|
137
146
|
}
|
|
@@ -141,7 +150,7 @@ async function createXCodeSandboxClient(options) {
|
|
|
141
150
|
return (0, exec_client_1.exec)({ command: 'xcodebuild', ...(opts && { xcodebuild: opts }) }, {
|
|
142
151
|
apiUrl: options.apiUrl,
|
|
143
152
|
token: options.token,
|
|
144
|
-
log
|
|
153
|
+
log,
|
|
145
154
|
});
|
|
146
155
|
},
|
|
147
156
|
};
|
package/sandbox-client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox-client.js","sourceRoot":"","sources":["src/sandbox-client.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"sandbox-client.js","sourceRoot":"","sources":["src/sandbox-client.ts"],"names":[],"mappings":"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"}
|
package/sandbox-client.mjs
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import os from 'os';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { syncFolder as syncFolderImpl } from "./folder-sync.mjs";
|
|
2
4
|
import { exec } from "./exec-client.mjs";
|
|
5
|
+
import { createIgnoreFn } from "./folder-sync-ignore.mjs";
|
|
6
|
+
import crypto from 'crypto';
|
|
3
7
|
/**
|
|
4
8
|
* Creates a client for interacting with a sandboxed Xcode build service.
|
|
5
9
|
*
|
|
@@ -46,7 +50,7 @@ export async function createXCodeSandboxClient(options) {
|
|
|
46
50
|
console.error('[XCodeSandbox]', ...args);
|
|
47
51
|
},
|
|
48
52
|
};
|
|
49
|
-
const
|
|
53
|
+
const log = (level, msg) => {
|
|
50
54
|
switch (level) {
|
|
51
55
|
case 'debug':
|
|
52
56
|
logger.debug(msg);
|
|
@@ -86,47 +90,51 @@ export async function createXCodeSandboxClient(options) {
|
|
|
86
90
|
}
|
|
87
91
|
return {
|
|
88
92
|
async sync(localCodePath, opts) {
|
|
93
|
+
// Use folder name and hash of absolute path to scope basisCacheDir uniquely for each sync root
|
|
94
|
+
const resolvedPath = path.resolve(localCodePath);
|
|
95
|
+
const folderName = path.basename(resolvedPath);
|
|
96
|
+
const hash = crypto.createHash('sha1').update(resolvedPath).digest('hex').slice(0, 8);
|
|
97
|
+
const cacheKey = `limsync-cache-${folderName}-${hash}`;
|
|
98
|
+
const basisCacheDir = opts?.basisCacheDir ?? path.join(os.tmpdir(), cacheKey);
|
|
89
99
|
const codeSyncOpts = {
|
|
90
100
|
apiUrl: options.apiUrl,
|
|
91
101
|
token: options.token,
|
|
92
|
-
udid:
|
|
93
|
-
install:
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
relativePath.startsWith('
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
102
|
+
udid: cacheKey,
|
|
103
|
+
install: opts?.install ?? true,
|
|
104
|
+
ignoreFn: await createIgnoreFn(localCodePath, {
|
|
105
|
+
basisCacheDir,
|
|
106
|
+
additional: (relativePath) => {
|
|
107
|
+
if (relativePath.startsWith('build/') ||
|
|
108
|
+
relativePath.startsWith('.build/') ||
|
|
109
|
+
relativePath.startsWith('DerivedData/') ||
|
|
110
|
+
relativePath.startsWith('Index.noindex/') ||
|
|
111
|
+
relativePath.startsWith('ModuleCache.noindex/') ||
|
|
112
|
+
relativePath.startsWith('.index-build/')) {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
if (relativePath.startsWith('.swiftpm/') ||
|
|
116
|
+
relativePath.startsWith('Pods/') ||
|
|
117
|
+
relativePath.startsWith('Carthage/Build/')) {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
if (relativePath.includes('/xcuserdata/')) {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
if (relativePath.includes('.dSYM/')) {
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
// User-provided ignores
|
|
127
|
+
if (opts?.ignore?.(relativePath)) {
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
101
130
|
return false;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
relativePath.startsWith('.limsync-cache/') ||
|
|
110
|
-
relativePath === '.DS_Store' ||
|
|
111
|
-
relativePath.endsWith('/.DS_Store')) {
|
|
112
|
-
return false;
|
|
113
|
-
}
|
|
114
|
-
if (relativePath.includes('/xcuserdata/')) {
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
if (relativePath.includes('.dSYM/')) {
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
// User-provided filter
|
|
121
|
-
if (opts?.filter && !opts.filter(relativePath)) {
|
|
122
|
-
return false;
|
|
123
|
-
}
|
|
124
|
-
return true;
|
|
125
|
-
},
|
|
126
|
-
...(opts?.basisCacheDir ? { basisCacheDir: opts.basisCacheDir } : {}),
|
|
127
|
-
...(opts?.maxPatchBytes !== undefined ? { maxPatchBytes: opts.maxPatchBytes } : {}),
|
|
128
|
-
...(opts?.watch !== undefined ? { watch: opts.watch } : {}),
|
|
129
|
-
log: opts?.log ?? logFn,
|
|
131
|
+
},
|
|
132
|
+
}),
|
|
133
|
+
basisCacheDir,
|
|
134
|
+
watch: opts?.watch ?? true,
|
|
135
|
+
maxPatchBytes: opts?.maxPatchBytes ?? 4 * 1024 * 1024,
|
|
136
|
+
launchMode: 'ForegroundIfRunning',
|
|
137
|
+
log,
|
|
130
138
|
};
|
|
131
139
|
const result = await syncFolderImpl(localCodePath, codeSyncOpts);
|
|
132
140
|
if (result.stopWatching) {
|
|
@@ -138,7 +146,7 @@ export async function createXCodeSandboxClient(options) {
|
|
|
138
146
|
return exec({ command: 'xcodebuild', ...(opts && { xcodebuild: opts }) }, {
|
|
139
147
|
apiUrl: options.apiUrl,
|
|
140
148
|
token: options.token,
|
|
141
|
-
log
|
|
149
|
+
log,
|
|
142
150
|
});
|
|
143
151
|
},
|
|
144
152
|
};
|
package/sandbox-client.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox-client.mjs","sourceRoot":"","sources":["src/sandbox-client.ts"],"names":[],"mappings":"OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"sandbox-client.mjs","sourceRoot":"","sources":["src/sandbox-client.ts"],"names":[],"mappings":"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"}
|
package/src/exec-client.ts
CHANGED
|
@@ -77,6 +77,7 @@ export class ReadableStream {
|
|
|
77
77
|
* @example
|
|
78
78
|
* // Stream-based (like Node.js spawn)
|
|
79
79
|
* const proc = exec({ command: 'xcodebuild' }, options);
|
|
80
|
+
* proc.command.on('data', (chunk) => console.log('[command]', chunk));
|
|
80
81
|
* proc.stdout.on('data', (chunk) => process.stdout.write(chunk));
|
|
81
82
|
* proc.stderr.on('data', (chunk) => process.stderr.write(chunk));
|
|
82
83
|
* proc.on('exit', (code) => console.log(`Exited with code ${code}`));
|
|
@@ -85,6 +86,9 @@ export class ReadableStream {
|
|
|
85
86
|
* const { exitCode, status } = await proc;
|
|
86
87
|
*/
|
|
87
88
|
export class ExecChildProcess implements PromiseLike<ExecResult> {
|
|
89
|
+
/** Command stream - emits the executed command and then closes */
|
|
90
|
+
readonly command = new ReadableStream();
|
|
91
|
+
|
|
88
92
|
/** Stdout stream - emits 'data' and 'close' events */
|
|
89
93
|
readonly stdout = new ReadableStream();
|
|
90
94
|
|
|
@@ -166,7 +170,6 @@ export class ExecChildProcess implements PromiseLike<ExecResult> {
|
|
|
166
170
|
const { apiUrl, token } = this.options;
|
|
167
171
|
|
|
168
172
|
// 1. Trigger the build via POST /exec
|
|
169
|
-
log('debug', `POST ${apiUrl}/exec`);
|
|
170
173
|
let execRes: Response;
|
|
171
174
|
try {
|
|
172
175
|
execRes = await fetch(`${apiUrl}/exec`, {
|
|
@@ -180,6 +183,7 @@ export class ExecChildProcess implements PromiseLike<ExecResult> {
|
|
|
180
183
|
});
|
|
181
184
|
} catch (err) {
|
|
182
185
|
if (this.killed) {
|
|
186
|
+
this.command.emit('close');
|
|
183
187
|
this.stdout.emit('close');
|
|
184
188
|
this.stderr.emit('close');
|
|
185
189
|
for (const listener of this.exitListeners) {
|
|
@@ -197,11 +201,10 @@ export class ExecChildProcess implements PromiseLike<ExecResult> {
|
|
|
197
201
|
|
|
198
202
|
const execData = (await execRes.json()) as { execId: string };
|
|
199
203
|
this.execId = execData.execId;
|
|
200
|
-
log('
|
|
204
|
+
log('debug', `Build started: ${this.execId}`);
|
|
201
205
|
|
|
202
206
|
// 2. Stream logs via SSE and wait for exit code
|
|
203
207
|
const eventsUrl = `${apiUrl}/exec/${this.execId}/events`;
|
|
204
|
-
log('debug', `GET ${eventsUrl} (SSE)`);
|
|
205
208
|
|
|
206
209
|
const timeoutMs = 3600 * 1000; // 1 hour max
|
|
207
210
|
let exitCode: number;
|
|
@@ -215,7 +218,7 @@ export class ExecChildProcess implements PromiseLike<ExecResult> {
|
|
|
215
218
|
]);
|
|
216
219
|
} catch {
|
|
217
220
|
if (this.killed) {
|
|
218
|
-
log('
|
|
221
|
+
log('debug', 'Build killed');
|
|
219
222
|
exitCode = -1;
|
|
220
223
|
} else {
|
|
221
224
|
log('warn', 'SSE completion timeout');
|
|
@@ -230,6 +233,7 @@ export class ExecChildProcess implements PromiseLike<ExecResult> {
|
|
|
230
233
|
}
|
|
231
234
|
|
|
232
235
|
// Emit close events on streams
|
|
236
|
+
this.command.emit('close');
|
|
233
237
|
this.stdout.emit('close');
|
|
234
238
|
this.stderr.emit('close');
|
|
235
239
|
|
|
@@ -250,12 +254,12 @@ export class ExecChildProcess implements PromiseLike<ExecResult> {
|
|
|
250
254
|
status,
|
|
251
255
|
};
|
|
252
256
|
|
|
253
|
-
this.log('
|
|
257
|
+
this.log('debug', `Build finished: ${result.status} (exit ${result.exitCode})`);
|
|
254
258
|
return result;
|
|
255
259
|
}
|
|
256
260
|
|
|
257
261
|
/**
|
|
258
|
-
* Opens an SSE connection and routes events to stdout/stderr streams.
|
|
262
|
+
* Opens an SSE connection and routes streamed events to the exposed command/stdout/stderr streams.
|
|
259
263
|
* Resolves with the exit code when an 'exitCode' event arrives.
|
|
260
264
|
* Rejects when the abort signal fires (kill or cleanup).
|
|
261
265
|
*/
|
|
@@ -273,7 +277,9 @@ export class ExecChildProcess implements PromiseLike<ExecResult> {
|
|
|
273
277
|
onMessage: (message: EventSourceMessage) => {
|
|
274
278
|
const data = typeof message.data === 'string' ? message.data : String(message.data ?? '');
|
|
275
279
|
const eventType = message.event;
|
|
276
|
-
if (eventType === '
|
|
280
|
+
if (eventType === 'command') {
|
|
281
|
+
this.command.emit('data', data);
|
|
282
|
+
} else if (eventType === 'stdout') {
|
|
277
283
|
this.stdout.emit('data', data);
|
|
278
284
|
} else if (eventType === 'stderr') {
|
|
279
285
|
this.stderr.emit('data', data);
|
|
@@ -316,6 +322,7 @@ export class ExecChildProcess implements PromiseLike<ExecResult> {
|
|
|
316
322
|
* const proc = exec({ command: 'xcodebuild' }, { apiUrl: '...', token: '...' });
|
|
317
323
|
*
|
|
318
324
|
* // Stream output
|
|
325
|
+
* proc.command.on('data', (chunk) => console.log('[command]', chunk));
|
|
319
326
|
* proc.stdout.on('data', (chunk) => console.log('[stdout]', chunk));
|
|
320
327
|
* proc.stderr.on('data', (chunk) => console.error('[stderr]', chunk));
|
|
321
328
|
*
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import ignorePkg from 'ignore';
|
|
4
|
+
|
|
5
|
+
export type IgnoreFn = (relativePath: string) => boolean;
|
|
6
|
+
|
|
7
|
+
export type IgnoreFnOptions = {
|
|
8
|
+
additional?: IgnoreFn;
|
|
9
|
+
basisCacheDir: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
function normalizeRelativePath(relativePath: string): string {
|
|
13
|
+
return relativePath
|
|
14
|
+
.split(path.sep)
|
|
15
|
+
.join('/')
|
|
16
|
+
.replace(/^\.\/+/, '')
|
|
17
|
+
.replace(/\/+/g, '/');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function createIgnoreFn(rootDir: string, options: IgnoreFnOptions): Promise<IgnoreFn> {
|
|
21
|
+
const rootResolved = path.resolve(rootDir);
|
|
22
|
+
const ig = ignorePkg();
|
|
23
|
+
const gitignorePath = path.join(rootResolved, '.gitignore');
|
|
24
|
+
try {
|
|
25
|
+
const content = await fs.promises.readFile(gitignorePath, 'utf-8');
|
|
26
|
+
ig.add(content);
|
|
27
|
+
} catch {
|
|
28
|
+
// No .gitignore file, return empty ignore instance
|
|
29
|
+
}
|
|
30
|
+
const basisCacheRelative = normalizeRelativePath(
|
|
31
|
+
path.relative(rootResolved, options.basisCacheDir),
|
|
32
|
+
).replace(/\/+$/, '');
|
|
33
|
+
const shouldIgnoreBasisCache =
|
|
34
|
+
basisCacheRelative &&
|
|
35
|
+
basisCacheRelative !== '.' &&
|
|
36
|
+
basisCacheRelative !== '..' &&
|
|
37
|
+
!basisCacheRelative.startsWith('../');
|
|
38
|
+
|
|
39
|
+
return (relativePath: string) => {
|
|
40
|
+
const normalized = normalizeRelativePath(relativePath);
|
|
41
|
+
if (!normalized) return false;
|
|
42
|
+
const withoutTrailingSlash = normalized.replace(/\/+$/, '');
|
|
43
|
+
|
|
44
|
+
if (
|
|
45
|
+
withoutTrailingSlash === '.git' ||
|
|
46
|
+
withoutTrailingSlash.startsWith('.git/') ||
|
|
47
|
+
withoutTrailingSlash.endsWith('/.git') ||
|
|
48
|
+
withoutTrailingSlash.includes('/.git/') ||
|
|
49
|
+
withoutTrailingSlash === '.DS_Store' ||
|
|
50
|
+
withoutTrailingSlash.endsWith('/.DS_Store')
|
|
51
|
+
) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
if (
|
|
55
|
+
shouldIgnoreBasisCache &&
|
|
56
|
+
(withoutTrailingSlash === basisCacheRelative ||
|
|
57
|
+
withoutTrailingSlash.startsWith(`${basisCacheRelative}/`))
|
|
58
|
+
) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
if (ig.ignores(normalized)) return true;
|
|
62
|
+
if (options.additional?.(normalized)) return true;
|
|
63
|
+
return false;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import { IgnoreFn } from './folder-sync-ignore';
|
|
3
4
|
|
|
4
5
|
export type FolderSyncWatcherOptions = {
|
|
5
6
|
rootPath: string;
|
|
6
7
|
log?: (level: 'debug' | 'info' | 'warn' | 'error', msg: string) => void;
|
|
8
|
+
ignoreFn: IgnoreFn;
|
|
7
9
|
onChange: (reason: string) => void;
|
|
8
10
|
};
|
|
9
11
|
|
|
@@ -11,27 +13,6 @@ type WatcherHandle = { close: () => void };
|
|
|
11
13
|
|
|
12
14
|
const noopLogger = (_level: 'debug' | 'info' | 'warn' | 'error', _msg: string) => {};
|
|
13
15
|
|
|
14
|
-
async function listDirsRecursive(root: string): Promise<string[]> {
|
|
15
|
-
const dirs: string[] = [root];
|
|
16
|
-
const queue: string[] = [root];
|
|
17
|
-
while (queue.length) {
|
|
18
|
-
const dir = queue.pop()!;
|
|
19
|
-
let entries: fs.Dirent[];
|
|
20
|
-
try {
|
|
21
|
-
entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
|
22
|
-
} catch {
|
|
23
|
-
continue;
|
|
24
|
-
}
|
|
25
|
-
for (const ent of entries) {
|
|
26
|
-
if (!ent.isDirectory()) continue;
|
|
27
|
-
const full = path.join(dir, ent.name);
|
|
28
|
-
dirs.push(full);
|
|
29
|
-
queue.push(full);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return dirs;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
16
|
/**
|
|
36
17
|
* Watch a folder tree for changes. Uses recursive watch when supported (macOS),
|
|
37
18
|
* otherwise falls back to watching each directory. Debounced.
|
|
@@ -42,58 +23,22 @@ export async function watchFolderTree(opts: FolderSyncWatcherOptions): Promise<W
|
|
|
42
23
|
const log = opts.log ?? noopLogger;
|
|
43
24
|
const debounceMs = 500;
|
|
44
25
|
const rootPath = opts.rootPath;
|
|
45
|
-
|
|
46
26
|
if (!fs.existsSync(rootPath)) {
|
|
47
27
|
throw new Error(`watchFolderTree root does not exist: ${rootPath}`);
|
|
48
28
|
}
|
|
49
|
-
|
|
50
29
|
let timer: NodeJS.Timeout | undefined;
|
|
51
30
|
const schedule = (reason: string) => {
|
|
52
31
|
if (timer) clearTimeout(timer);
|
|
53
32
|
timer = setTimeout(() => opts.onChange(reason), debounceMs);
|
|
54
33
|
};
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
});
|
|
61
|
-
log('info', `watchFolderTree(recursive): ${rootPath}`);
|
|
62
|
-
return { close: () => watcher.close() };
|
|
63
|
-
} catch (err) {
|
|
64
|
-
log(
|
|
65
|
-
'warn',
|
|
66
|
-
`watchFolderTree: recursive unsupported, using per-directory watches: ${(err as Error).message}`,
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Fallback: watch every directory. Also re-scan on any event to pick up newly-created dirs.
|
|
71
|
-
const watchers = new Map<string, fs.FSWatcher>();
|
|
72
|
-
|
|
73
|
-
const ensureWatched = async () => {
|
|
74
|
-
const dirs = await listDirsRecursive(rootPath);
|
|
75
|
-
for (const d of dirs) {
|
|
76
|
-
if (watchers.has(d)) continue;
|
|
77
|
-
try {
|
|
78
|
-
const w = fs.watch(d, (_eventType, filename) => {
|
|
79
|
-
schedule(filename ? `change:${filename.toString()}` : 'change');
|
|
80
|
-
void ensureWatched();
|
|
81
|
-
});
|
|
82
|
-
watchers.set(d, w);
|
|
83
|
-
} catch {
|
|
84
|
-
// ignore dirs we can't watch
|
|
85
|
-
}
|
|
34
|
+
const watcher = fs.watch(rootPath, { recursive: true }, (_eventType, filename) => {
|
|
35
|
+
if (!filename) return;
|
|
36
|
+
const relativePath = filename.split(path.sep).join('/');
|
|
37
|
+
if (opts.ignoreFn(relativePath)) {
|
|
38
|
+
return;
|
|
86
39
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
close: () => {
|
|
94
|
-
if (timer) clearTimeout(timer);
|
|
95
|
-
for (const w of watchers.values()) w.close();
|
|
96
|
-
watchers.clear();
|
|
97
|
-
},
|
|
98
|
-
};
|
|
40
|
+
schedule(relativePath ? `change:${relativePath}` : 'change');
|
|
41
|
+
});
|
|
42
|
+
log('debug', `watchFolderTree(recursive): ${rootPath}`);
|
|
43
|
+
return { close: () => watcher.close() };
|
|
99
44
|
}
|