@hyperfrontend/project-scope 0.1.0 → 0.2.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 +15 -3
- package/README.md +2 -0
- package/analyze.d.ts.map +1 -1
- package/cli/commands/analyze.d.ts +8 -0
- package/cli/commands/analyze.d.ts.map +1 -1
- package/cli/commands/config.d.ts +7 -0
- package/cli/commands/config.d.ts.map +1 -1
- package/cli/commands/deps.d.ts +6 -0
- package/cli/commands/deps.d.ts.map +1 -1
- package/cli/commands/tree.d.ts +12 -0
- package/cli/commands/tree.d.ts.map +1 -1
- package/cli/index.cjs.js +73 -206
- package/cli/index.cjs.js.map +1 -1
- package/cli/index.esm.js +73 -206
- package/cli/index.esm.js.map +1 -1
- package/cli/run.d.ts.map +1 -1
- package/core/cache.d.ts +1 -0
- package/core/cache.d.ts.map +1 -1
- package/core/encoding/convert.d.ts.map +1 -1
- package/core/encoding/detect.d.ts +5 -0
- package/core/encoding/detect.d.ts.map +1 -1
- package/core/encoding/index.cjs.js +2 -23
- package/core/encoding/index.cjs.js.map +1 -1
- package/core/encoding/index.esm.js +2 -23
- package/core/encoding/index.esm.js.map +1 -1
- package/core/errors/structured-errors.d.ts +2 -0
- package/core/errors/structured-errors.d.ts.map +1 -1
- package/core/fs/directory.d.ts +3 -0
- package/core/fs/directory.d.ts.map +1 -1
- package/core/fs/index.cjs.js +0 -14
- package/core/fs/index.cjs.js.map +1 -1
- package/core/fs/index.esm.js +0 -14
- package/core/fs/index.esm.js.map +1 -1
- package/core/fs/read.d.ts +11 -3
- package/core/fs/read.d.ts.map +1 -1
- package/core/fs/traversal.d.ts.map +1 -1
- package/core/index.cjs.js +11 -63
- package/core/index.cjs.js.map +1 -1
- package/core/index.esm.js +11 -63
- package/core/index.esm.js.map +1 -1
- package/core/logger.d.ts.map +1 -1
- package/core/path/index.cjs.js +5 -2
- package/core/path/index.cjs.js.map +1 -1
- package/core/path/index.esm.js +5 -2
- package/core/path/index.esm.js.map +1 -1
- package/core/path/normalize.d.ts.map +1 -1
- package/core/patterns/glob.d.ts +0 -4
- package/core/patterns/glob.d.ts.map +1 -1
- package/core/platform/detect.d.ts.map +1 -1
- package/core/platform/index.cjs.js +0 -10
- package/core/platform/index.cjs.js.map +1 -1
- package/core/platform/index.esm.js +0 -10
- package/core/platform/index.esm.js.map +1 -1
- package/core/platform/line-endings.d.ts.map +1 -1
- package/heuristics/dependencies/analyze.d.ts.map +1 -1
- package/heuristics/dependencies/index.cjs.js +0 -17
- package/heuristics/dependencies/index.cjs.js.map +1 -1
- package/heuristics/dependencies/index.esm.js +0 -17
- package/heuristics/dependencies/index.esm.js.map +1 -1
- package/heuristics/entry-points/discover.d.ts +34 -7
- package/heuristics/entry-points/discover.d.ts.map +1 -1
- package/heuristics/entry-points/index.cjs.js +6 -34
- package/heuristics/entry-points/index.cjs.js.map +1 -1
- package/heuristics/entry-points/index.esm.js +6 -34
- package/heuristics/entry-points/index.esm.js.map +1 -1
- package/heuristics/framework/index.cjs.js +1 -63
- package/heuristics/framework/index.cjs.js.map +1 -1
- package/heuristics/framework/index.esm.js +1 -63
- package/heuristics/framework/index.esm.js.map +1 -1
- package/heuristics/index.cjs.js +7 -88
- package/heuristics/index.cjs.js.map +1 -1
- package/heuristics/index.esm.js +7 -88
- package/heuristics/index.esm.js.map +1 -1
- package/heuristics/project-type/index.cjs.js +1 -63
- package/heuristics/project-type/index.cjs.js.map +1 -1
- package/heuristics/project-type/index.esm.js +1 -63
- package/heuristics/project-type/index.esm.js.map +1 -1
- package/index.cjs.js +86 -294
- package/index.cjs.js.map +1 -1
- package/index.esm.js +86 -294
- package/index.esm.js.map +1 -1
- package/nx/detect.d.ts.map +1 -1
- package/nx/devkit-loader.d.ts.map +1 -1
- package/nx/index.cjs.js +0 -29
- package/nx/index.cjs.js.map +1 -1
- package/nx/index.esm.js +0 -29
- package/nx/index.esm.js.map +1 -1
- package/nx/project-config.d.ts +2 -0
- package/nx/project-config.d.ts.map +1 -1
- package/package.json +9 -9
- package/project/config/index.cjs.js +4 -46
- package/project/config/index.cjs.js.map +1 -1
- package/project/config/index.esm.js +4 -46
- package/project/config/index.esm.js.map +1 -1
- package/project/config/patterns.d.ts.map +1 -1
- package/project/index.cjs.js +4 -47
- package/project/index.cjs.js.map +1 -1
- package/project/index.esm.js +4 -47
- package/project/index.esm.js.map +1 -1
- package/project/package/index.cjs.js +0 -11
- package/project/package/index.cjs.js.map +1 -1
- package/project/package/index.esm.js +0 -11
- package/project/package/index.esm.js.map +1 -1
- package/project/package/read.d.ts +1 -0
- package/project/package/read.d.ts.map +1 -1
- package/project/root/index.cjs.js +0 -11
- package/project/root/index.cjs.js.map +1 -1
- package/project/root/index.esm.js +0 -11
- package/project/root/index.esm.js.map +1 -1
- package/project/traversal/index.cjs.js +4 -28
- package/project/traversal/index.cjs.js.map +1 -1
- package/project/traversal/index.esm.js +4 -28
- package/project/traversal/index.esm.js.map +1 -1
- package/tech/backend/express.d.ts.map +1 -1
- package/tech/backend/fastify.d.ts.map +1 -1
- package/tech/backend/index.cjs.js +0 -17
- package/tech/backend/index.cjs.js.map +1 -1
- package/tech/backend/index.esm.js +0 -17
- package/tech/backend/index.esm.js.map +1 -1
- package/tech/backend/koa.d.ts.map +1 -1
- package/tech/backend/nestjs.d.ts.map +1 -1
- package/tech/build/index.cjs.js +0 -12
- package/tech/build/index.cjs.js.map +1 -1
- package/tech/build/index.esm.js +0 -12
- package/tech/build/index.esm.js.map +1 -1
- package/tech/frontend/index.cjs.js +0 -16
- package/tech/frontend/index.cjs.js.map +1 -1
- package/tech/frontend/index.esm.js +0 -16
- package/tech/frontend/index.esm.js.map +1 -1
- package/tech/frontend/qwik.d.ts.map +1 -1
- package/tech/frontend/remix.d.ts.map +1 -1
- package/tech/frontend/sveltekit.d.ts.map +1 -1
- package/tech/index.cjs.js +1 -63
- package/tech/index.cjs.js.map +1 -1
- package/tech/index.d.ts.map +1 -1
- package/tech/index.esm.js +1 -63
- package/tech/index.esm.js.map +1 -1
- package/tech/legacy/angularjs.d.ts.map +1 -1
- package/tech/legacy/backbone.d.ts.map +1 -1
- package/tech/legacy/ember.d.ts.map +1 -1
- package/tech/legacy/index.cjs.js +0 -26
- package/tech/legacy/index.cjs.js.map +1 -1
- package/tech/legacy/index.esm.js +0 -26
- package/tech/legacy/index.esm.js.map +1 -1
- package/tech/legacy/jquery.d.ts.map +1 -1
- package/tech/linting/biome.d.ts.map +1 -1
- package/tech/linting/index.cjs.js +0 -14
- package/tech/linting/index.cjs.js.map +1 -1
- package/tech/linting/index.esm.js +0 -14
- package/tech/linting/index.esm.js.map +1 -1
- package/tech/linting/prettier.d.ts.map +1 -1
- package/tech/monorepo/index.cjs.js +1 -13
- package/tech/monorepo/index.cjs.js.map +1 -1
- package/tech/monorepo/index.esm.js +1 -13
- package/tech/monorepo/index.esm.js.map +1 -1
- package/tech/monorepo/types.d.ts +1 -1
- package/tech/monorepo/types.d.ts.map +1 -1
- package/tech/shared-utils/detector-helpers.d.ts.map +1 -1
- package/tech/testing/index.cjs.js +0 -12
- package/tech/testing/index.cjs.js.map +1 -1
- package/tech/testing/index.esm.js +0 -12
- package/tech/testing/index.esm.js.map +1 -1
- package/tech/types/detectors.d.ts.map +1 -1
- package/tech/types/index.cjs.js +0 -27
- package/tech/types/index.cjs.js.map +1 -1
- package/tech/types/index.esm.js +0 -27
- package/tech/types/index.esm.js.map +1 -1
- package/vfs/commit.d.ts.map +1 -1
- package/vfs/diff.d.ts.map +1 -1
- package/vfs/factory.d.ts.map +1 -1
- package/vfs/fs-tree.d.ts.map +1 -1
- package/vfs/index.cjs.js +5 -46
- package/vfs/index.cjs.js.map +1 -1
- package/vfs/index.esm.js +5 -46
- package/vfs/index.esm.js.map +1 -1
- package/vfs/types.d.ts +1 -0
- package/vfs/types.d.ts.map +1 -1
package/index.esm.js
CHANGED
|
@@ -11,7 +11,6 @@ import { tmpdir, platform, arch } from 'node:os';
|
|
|
11
11
|
*
|
|
12
12
|
* @module @hyperfrontend/immutable-api-utils/built-in-copy/console
|
|
13
13
|
*/
|
|
14
|
-
// Capture references at module initialization time
|
|
15
14
|
const _console = globalThis.console;
|
|
16
15
|
/**
|
|
17
16
|
* (Safe copy) Outputs a message to the console.
|
|
@@ -86,44 +85,6 @@ _console.timeEnd.bind(_console);
|
|
|
86
85
|
*/
|
|
87
86
|
_console.timeLog.bind(_console);
|
|
88
87
|
|
|
89
|
-
/**
|
|
90
|
-
* Safe copies of Array built-in static methods.
|
|
91
|
-
*
|
|
92
|
-
* These references are captured at module initialization time to protect against
|
|
93
|
-
* prototype pollution attacks. Import only what you need for tree-shaking.
|
|
94
|
-
*
|
|
95
|
-
* @module @hyperfrontend/immutable-api-utils/built-in-copy/array
|
|
96
|
-
*/
|
|
97
|
-
// Capture references at module initialization time
|
|
98
|
-
const _Array = globalThis.Array;
|
|
99
|
-
/**
|
|
100
|
-
* (Safe copy) Determines whether the passed value is an Array.
|
|
101
|
-
*/
|
|
102
|
-
const isArray = _Array.isArray;
|
|
103
|
-
/**
|
|
104
|
-
* (Safe copy) Creates an array from an array-like or iterable object.
|
|
105
|
-
*/
|
|
106
|
-
const from = _Array.from;
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Safe copies of JSON built-in methods.
|
|
110
|
-
*
|
|
111
|
-
* These references are captured at module initialization time to protect against
|
|
112
|
-
* prototype pollution attacks. Import only what you need for tree-shaking.
|
|
113
|
-
*
|
|
114
|
-
* @module @hyperfrontend/immutable-api-utils/built-in-copy/json
|
|
115
|
-
*/
|
|
116
|
-
// Capture references at module initialization time
|
|
117
|
-
const _JSON = globalThis.JSON;
|
|
118
|
-
/**
|
|
119
|
-
* (Safe copy) Converts a JavaScript Object Notation (JSON) string into an object.
|
|
120
|
-
*/
|
|
121
|
-
const parse = _JSON.parse;
|
|
122
|
-
/**
|
|
123
|
-
* (Safe copy) Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
|
|
124
|
-
*/
|
|
125
|
-
const stringify = _JSON.stringify;
|
|
126
|
-
|
|
127
88
|
/**
|
|
128
89
|
* Safe copies of Object built-in methods.
|
|
129
90
|
*
|
|
@@ -132,7 +93,6 @@ const stringify = _JSON.stringify;
|
|
|
132
93
|
*
|
|
133
94
|
* @module @hyperfrontend/immutable-api-utils/built-in-copy/object
|
|
134
95
|
*/
|
|
135
|
-
// Capture references at module initialization time
|
|
136
96
|
const _Object = globalThis.Object;
|
|
137
97
|
/**
|
|
138
98
|
* (Safe copy) Prevents modification of existing property attributes and values,
|
|
@@ -160,30 +120,25 @@ const defineProperty = _Object.defineProperty;
|
|
|
160
120
|
*/
|
|
161
121
|
const defineProperties = _Object.defineProperties;
|
|
162
122
|
|
|
123
|
+
const registeredClasses = [];
|
|
124
|
+
|
|
163
125
|
/**
|
|
164
|
-
* Safe copies of
|
|
165
|
-
*
|
|
166
|
-
* Since constructors cannot be safely captured via Object.assign, this module
|
|
167
|
-
* provides a factory function that uses Reflect.construct internally.
|
|
126
|
+
* Safe copies of Array built-in static methods.
|
|
168
127
|
*
|
|
169
128
|
* These references are captured at module initialization time to protect against
|
|
170
129
|
* prototype pollution attacks. Import only what you need for tree-shaking.
|
|
171
130
|
*
|
|
172
|
-
* @module @hyperfrontend/immutable-api-utils/built-in-copy/
|
|
131
|
+
* @module @hyperfrontend/immutable-api-utils/built-in-copy/array
|
|
173
132
|
*/
|
|
174
|
-
|
|
175
|
-
const _Set = globalThis.Set;
|
|
176
|
-
const _Reflect$3 = globalThis.Reflect;
|
|
133
|
+
const _Array = globalThis.Array;
|
|
177
134
|
/**
|
|
178
|
-
* (Safe copy)
|
|
179
|
-
* Use this instead of `new Set()`.
|
|
180
|
-
*
|
|
181
|
-
* @param iterable - Optional iterable of values.
|
|
182
|
-
* @returns A new Set instance.
|
|
135
|
+
* (Safe copy) Determines whether the passed value is an Array.
|
|
183
136
|
*/
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
137
|
+
const isArray = _Array.isArray;
|
|
138
|
+
/**
|
|
139
|
+
* (Safe copy) Creates an array from an array-like or iterable object.
|
|
140
|
+
*/
|
|
141
|
+
const from = _Array.from;
|
|
187
142
|
|
|
188
143
|
/**
|
|
189
144
|
* Returns the data type of the target.
|
|
@@ -219,9 +174,8 @@ const getType = (target) => {
|
|
|
219
174
|
*
|
|
220
175
|
* @module @hyperfrontend/immutable-api-utils/built-in-copy/error
|
|
221
176
|
*/
|
|
222
|
-
// Capture references at module initialization time
|
|
223
177
|
const _Error = globalThis.Error;
|
|
224
|
-
const _Reflect$
|
|
178
|
+
const _Reflect$3 = globalThis.Reflect;
|
|
225
179
|
/**
|
|
226
180
|
* (Safe copy) Creates a new Error using the captured Error constructor.
|
|
227
181
|
* Use this instead of `new Error()`.
|
|
@@ -230,7 +184,7 @@ const _Reflect$2 = globalThis.Reflect;
|
|
|
230
184
|
* @param options - Optional error options.
|
|
231
185
|
* @returns A new Error instance.
|
|
232
186
|
*/
|
|
233
|
-
const createError = (message, options) => _Reflect$
|
|
187
|
+
const createError = (message, options) => _Reflect$3.construct(_Error, [message, options]);
|
|
234
188
|
|
|
235
189
|
/**
|
|
236
190
|
* Safe copies of Map built-in via factory function.
|
|
@@ -243,9 +197,8 @@ const createError = (message, options) => _Reflect$2.construct(_Error, [message,
|
|
|
243
197
|
*
|
|
244
198
|
* @module @hyperfrontend/immutable-api-utils/built-in-copy/map
|
|
245
199
|
*/
|
|
246
|
-
// Capture references at module initialization time
|
|
247
200
|
const _Map = globalThis.Map;
|
|
248
|
-
const _Reflect$
|
|
201
|
+
const _Reflect$2 = globalThis.Reflect;
|
|
249
202
|
/**
|
|
250
203
|
* (Safe copy) Creates a new Map using the captured Map constructor.
|
|
251
204
|
* Use this instead of `new Map()`.
|
|
@@ -253,7 +206,7 @@ const _Reflect$1 = globalThis.Reflect;
|
|
|
253
206
|
* @param iterable - Optional iterable of key-value pairs.
|
|
254
207
|
* @returns A new Map instance.
|
|
255
208
|
*/
|
|
256
|
-
const createMap = (iterable) => _Reflect$
|
|
209
|
+
const createMap = (iterable) => _Reflect$2.construct(_Map, iterable ? [iterable] : []);
|
|
257
210
|
|
|
258
211
|
/**
|
|
259
212
|
* Safe copies of Date built-in via factory function and static methods.
|
|
@@ -266,11 +219,10 @@ const createMap = (iterable) => _Reflect$1.construct(_Map, iterable ? [iterable]
|
|
|
266
219
|
*
|
|
267
220
|
* @module @hyperfrontend/immutable-api-utils/built-in-copy/date
|
|
268
221
|
*/
|
|
269
|
-
// Capture references at module initialization time
|
|
270
222
|
const _Date = globalThis.Date;
|
|
271
|
-
const _Reflect = globalThis.Reflect;
|
|
223
|
+
const _Reflect$1 = globalThis.Reflect;
|
|
272
224
|
function createDate(...args) {
|
|
273
|
-
return _Reflect.construct(_Date, args);
|
|
225
|
+
return _Reflect$1.construct(_Date, args);
|
|
274
226
|
}
|
|
275
227
|
/**
|
|
276
228
|
* (Safe copy) Returns the number of milliseconds elapsed since January 1, 1970 00:00:00 UTC.
|
|
@@ -285,15 +237,11 @@ const dateNow = _Date.now;
|
|
|
285
237
|
*
|
|
286
238
|
* @module @hyperfrontend/immutable-api-utils/built-in-copy/math
|
|
287
239
|
*/
|
|
288
|
-
// Capture references at module initialization time
|
|
289
240
|
const _Math = globalThis.Math;
|
|
290
241
|
/**
|
|
291
242
|
* (Safe copy) Returns the value of a number rounded to the nearest integer.
|
|
292
243
|
*/
|
|
293
244
|
const round = _Math.round;
|
|
294
|
-
// ============================================================================
|
|
295
|
-
// Min/Max
|
|
296
|
-
// ============================================================================
|
|
297
245
|
/**
|
|
298
246
|
* (Safe copy) Returns the larger of zero or more numbers.
|
|
299
247
|
*/
|
|
@@ -303,6 +251,28 @@ const max = _Math.max;
|
|
|
303
251
|
*/
|
|
304
252
|
const min = _Math.min;
|
|
305
253
|
|
|
254
|
+
/**
|
|
255
|
+
* Safe copies of Set built-in via factory function.
|
|
256
|
+
*
|
|
257
|
+
* Since constructors cannot be safely captured via Object.assign, this module
|
|
258
|
+
* provides a factory function that uses Reflect.construct internally.
|
|
259
|
+
*
|
|
260
|
+
* These references are captured at module initialization time to protect against
|
|
261
|
+
* prototype pollution attacks. Import only what you need for tree-shaking.
|
|
262
|
+
*
|
|
263
|
+
* @module @hyperfrontend/immutable-api-utils/built-in-copy/set
|
|
264
|
+
*/
|
|
265
|
+
const _Set = globalThis.Set;
|
|
266
|
+
const _Reflect = globalThis.Reflect;
|
|
267
|
+
/**
|
|
268
|
+
* (Safe copy) Creates a new Set using the captured Set constructor.
|
|
269
|
+
* Use this instead of `new Set()`.
|
|
270
|
+
*
|
|
271
|
+
* @param iterable - Optional iterable of values.
|
|
272
|
+
* @returns A new Set instance.
|
|
273
|
+
*/
|
|
274
|
+
const createSet = (iterable) => _Reflect.construct(_Set, iterable ? [iterable] : []);
|
|
275
|
+
|
|
306
276
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
307
277
|
/**
|
|
308
278
|
* Creates a wrapper function that only executes the wrapped function if the condition function returns true.
|
|
@@ -466,6 +436,24 @@ function notFnMsg(label) {
|
|
|
466
436
|
|
|
467
437
|
createLogger(error, warn, log, info, debug);
|
|
468
438
|
|
|
439
|
+
/**
|
|
440
|
+
* Safe copies of JSON built-in methods.
|
|
441
|
+
*
|
|
442
|
+
* These references are captured at module initialization time to protect against
|
|
443
|
+
* prototype pollution attacks. Import only what you need for tree-shaking.
|
|
444
|
+
*
|
|
445
|
+
* @module @hyperfrontend/immutable-api-utils/built-in-copy/json
|
|
446
|
+
*/
|
|
447
|
+
const _JSON = globalThis.JSON;
|
|
448
|
+
/**
|
|
449
|
+
* (Safe copy) Converts a JavaScript Object Notation (JSON) string into an object.
|
|
450
|
+
*/
|
|
451
|
+
const parse = _JSON.parse;
|
|
452
|
+
/**
|
|
453
|
+
* (Safe copy) Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
|
|
454
|
+
*/
|
|
455
|
+
const stringify = _JSON.stringify;
|
|
456
|
+
|
|
469
457
|
/**
|
|
470
458
|
* Global log level registry.
|
|
471
459
|
* Tracks all created scoped loggers to allow global log level changes.
|
|
@@ -609,14 +597,11 @@ function formatMessage(namespace, message, meta) {
|
|
|
609
597
|
*/
|
|
610
598
|
function createScopedLogger(namespace, options = {}) {
|
|
611
599
|
const { level = 'error', sanitizeSecrets = true } = options;
|
|
612
|
-
// Create wrapper functions that add namespace prefix and sanitization
|
|
613
600
|
const createLogFn = (baseFn) => (message, meta) => {
|
|
614
601
|
const processedMeta = sanitizeSecrets && meta ? sanitize(meta) : meta;
|
|
615
602
|
baseFn(formatMessage(namespace, message, processedMeta));
|
|
616
603
|
};
|
|
617
|
-
// Create base logger with wrapped functions
|
|
618
604
|
const baseLogger = createLogger(createLogFn(error), createLogFn(warn), createLogFn(log), createLogFn(info), createLogFn(debug));
|
|
619
|
-
// Set initial log level (use global override if set)
|
|
620
605
|
baseLogger.setLogLevel(globalLogLevel ?? level);
|
|
621
606
|
const scopedLogger = freeze({
|
|
622
607
|
error: (message, meta) => baseLogger.error(message, meta),
|
|
@@ -627,7 +612,6 @@ function createScopedLogger(namespace, options = {}) {
|
|
|
627
612
|
setLogLevel: baseLogger.setLogLevel,
|
|
628
613
|
getLogLevel: baseLogger.getLogLevel,
|
|
629
614
|
});
|
|
630
|
-
// Register logger for global level management
|
|
631
615
|
loggerRegistry.add(scopedLogger);
|
|
632
616
|
return scopedLogger;
|
|
633
617
|
}
|
|
@@ -1063,17 +1047,14 @@ function readDirectoryRecursive(dirPath, options) {
|
|
|
1063
1047
|
entries = readDirectory(currentPath);
|
|
1064
1048
|
}
|
|
1065
1049
|
catch {
|
|
1066
|
-
// Skip inaccessible directories
|
|
1067
1050
|
fsDirLogger.debug('Skipping inaccessible directory', { path: currentPath });
|
|
1068
1051
|
return;
|
|
1069
1052
|
}
|
|
1070
1053
|
for (const entry of entries) {
|
|
1071
|
-
// Skip hidden files/dirs if not included
|
|
1072
1054
|
if (!includeHidden && entry.name.startsWith('.')) {
|
|
1073
1055
|
continue;
|
|
1074
1056
|
}
|
|
1075
1057
|
results.push({ ...entry, depth });
|
|
1076
|
-
// Recurse into directories
|
|
1077
1058
|
if (entry.isDirectory || (entry.isSymlink && followSymlinks && isDirectory(entry.path))) {
|
|
1078
1059
|
walk(entry.path, depth + 1);
|
|
1079
1060
|
}
|
|
@@ -1141,7 +1122,6 @@ function joinPosix(...paths) {
|
|
|
1141
1122
|
function normalizePath(filePath) {
|
|
1142
1123
|
if (!filePath)
|
|
1143
1124
|
return '';
|
|
1144
|
-
// Normalize path and convert backslashes to forward slashes
|
|
1145
1125
|
const normalized = normalize(filePath);
|
|
1146
1126
|
return sep === '\\' ? normalized.replace(/\\/g, '/') : normalized;
|
|
1147
1127
|
}
|
|
@@ -1175,7 +1155,11 @@ function normalizeToNative(filePath) {
|
|
|
1175
1155
|
* @returns Path with trailing slashes removed
|
|
1176
1156
|
*/
|
|
1177
1157
|
function removeTrailingSlash(filePath) {
|
|
1178
|
-
|
|
1158
|
+
let i = filePath.length;
|
|
1159
|
+
while (i > 0 && (filePath[i - 1] === '/' || filePath[i - 1] === '\\')) {
|
|
1160
|
+
i--;
|
|
1161
|
+
}
|
|
1162
|
+
return filePath.slice(0, i);
|
|
1179
1163
|
}
|
|
1180
1164
|
/**
|
|
1181
1165
|
* Append a forward slash to the path if not already present.
|
|
@@ -1349,7 +1333,6 @@ function traverseUpward(startPath, predicate) {
|
|
|
1349
1333
|
}
|
|
1350
1334
|
currentPath = dirname(currentPath);
|
|
1351
1335
|
}
|
|
1352
|
-
// Check root directory
|
|
1353
1336
|
if (predicate(rootPath)) {
|
|
1354
1337
|
fsTraversalLogger.debug('Upward traversal found match at root', { startPath, foundPath: rootPath });
|
|
1355
1338
|
return rootPath;
|
|
@@ -1721,29 +1704,23 @@ const depsLogger = createScopedLogger('project-scope:heuristics:deps');
|
|
|
1721
1704
|
*/
|
|
1722
1705
|
function extractImports(content) {
|
|
1723
1706
|
const imports = [];
|
|
1724
|
-
// ES import with 'from': import X from 'path' or import { X } from 'path'
|
|
1725
|
-
// Use non-greedy match and avoid nested quantifiers by matching "from" keyword directly
|
|
1726
1707
|
const esImportFromRegex = /import\s+.+?\s+from\s+['"]([^'"]+)['"]/g;
|
|
1727
1708
|
let match;
|
|
1728
1709
|
while ((match = esImportFromRegex.exec(content)) !== null) {
|
|
1729
1710
|
imports.push(match[1]);
|
|
1730
1711
|
}
|
|
1731
|
-
// Side-effect import: import 'path'
|
|
1732
1712
|
const sideEffectImportRegex = /import\s+['"]([^'"]+)['"]/g;
|
|
1733
1713
|
while ((match = sideEffectImportRegex.exec(content)) !== null) {
|
|
1734
1714
|
imports.push(match[1]);
|
|
1735
1715
|
}
|
|
1736
|
-
// Dynamic import: import('path')
|
|
1737
1716
|
const dynamicImportRegex = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
1738
1717
|
while ((match = dynamicImportRegex.exec(content)) !== null) {
|
|
1739
1718
|
imports.push(match[1]);
|
|
1740
1719
|
}
|
|
1741
|
-
// require: require('path')
|
|
1742
1720
|
const requireRegex = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
1743
1721
|
while ((match = requireRegex.exec(content)) !== null) {
|
|
1744
1722
|
imports.push(match[1]);
|
|
1745
1723
|
}
|
|
1746
|
-
// Re-export: export * from 'path' or export { X } from 'path'
|
|
1747
1724
|
const exportFromRegex = /export\s+.+?\s+from\s+['"]([^'"]+)['"]/g;
|
|
1748
1725
|
while ((match = exportFromRegex.exec(content)) !== null) {
|
|
1749
1726
|
imports.push(match[1]);
|
|
@@ -2057,7 +2034,6 @@ const cacheRegistry = createSet();
|
|
|
2057
2034
|
function createCache(options) {
|
|
2058
2035
|
const { ttl, maxSize } = options ?? {};
|
|
2059
2036
|
const store = createMap();
|
|
2060
|
-
// Track insertion order for FIFO eviction
|
|
2061
2037
|
const insertionOrder = [];
|
|
2062
2038
|
/**
|
|
2063
2039
|
* Check if an entry is expired.
|
|
@@ -2108,12 +2084,10 @@ function createCache(options) {
|
|
|
2108
2084
|
return entry.value;
|
|
2109
2085
|
},
|
|
2110
2086
|
set(key, value) {
|
|
2111
|
-
// If key exists, remove from order first
|
|
2112
2087
|
if (store.has(key)) {
|
|
2113
2088
|
removeFromOrder(key);
|
|
2114
2089
|
}
|
|
2115
2090
|
else {
|
|
2116
|
-
// Evict if needed before adding new entry
|
|
2117
2091
|
evictIfNeeded();
|
|
2118
2092
|
}
|
|
2119
2093
|
// eslint-disable-next-line workspace/no-unsafe-builtin-methods -- Date.now() is needed for Jest fake timers compatibility
|
|
@@ -2146,7 +2120,6 @@ function createCache(options) {
|
|
|
2146
2120
|
return [...insertionOrder];
|
|
2147
2121
|
},
|
|
2148
2122
|
};
|
|
2149
|
-
// Register cache for global operations
|
|
2150
2123
|
cacheRegistry.add(cache);
|
|
2151
2124
|
return freeze(cache);
|
|
2152
2125
|
}
|
|
@@ -2226,7 +2199,6 @@ function memoize(fn, options) {
|
|
|
2226
2199
|
cache.set(key, result);
|
|
2227
2200
|
return result;
|
|
2228
2201
|
};
|
|
2229
|
-
// Attach cache for direct access
|
|
2230
2202
|
defineProperty(memoized, 'cache', {
|
|
2231
2203
|
value: cache,
|
|
2232
2204
|
writable: false,
|
|
@@ -2235,10 +2207,6 @@ function memoize(fn, options) {
|
|
|
2235
2207
|
return memoized;
|
|
2236
2208
|
}
|
|
2237
2209
|
|
|
2238
|
-
/**
|
|
2239
|
-
* Pattern matching utilities with ReDoS protection.
|
|
2240
|
-
* Uses character-by-character matching instead of regex where possible.
|
|
2241
|
-
*/
|
|
2242
2210
|
/**
|
|
2243
2211
|
* Match path against glob pattern using safe character iteration.
|
|
2244
2212
|
* Avoids regex to prevent ReDoS attacks.
|
|
@@ -2276,17 +2244,14 @@ function matchGlobPattern(path, pattern) {
|
|
|
2276
2244
|
* @returns True if remaining segments match
|
|
2277
2245
|
*/
|
|
2278
2246
|
function matchSegments(pathParts, patternParts, pathIdx, patternIdx) {
|
|
2279
|
-
// Base cases
|
|
2280
2247
|
if (pathIdx === pathParts.length && patternIdx === patternParts.length) {
|
|
2281
|
-
return true;
|
|
2248
|
+
return true;
|
|
2282
2249
|
}
|
|
2283
2250
|
if (patternIdx >= patternParts.length) {
|
|
2284
|
-
return false;
|
|
2251
|
+
return false;
|
|
2285
2252
|
}
|
|
2286
2253
|
const patternPart = patternParts[patternIdx];
|
|
2287
|
-
// Handle ** (globstar) - matches zero or more directories
|
|
2288
2254
|
if (patternPart === '**') {
|
|
2289
|
-
// Try matching rest of pattern against current position and all future positions
|
|
2290
2255
|
for (let i = pathIdx; i <= pathParts.length; i++) {
|
|
2291
2256
|
if (matchSegments(pathParts, patternParts, i, patternIdx + 1)) {
|
|
2292
2257
|
return true;
|
|
@@ -2295,10 +2260,9 @@ function matchSegments(pathParts, patternParts, pathIdx, patternIdx) {
|
|
|
2295
2260
|
return false;
|
|
2296
2261
|
}
|
|
2297
2262
|
if (pathIdx >= pathParts.length) {
|
|
2298
|
-
return false;
|
|
2263
|
+
return false;
|
|
2299
2264
|
}
|
|
2300
2265
|
const pathPart = pathParts[pathIdx];
|
|
2301
|
-
// Match current segment
|
|
2302
2266
|
if (matchSegment(pathPart, patternPart)) {
|
|
2303
2267
|
return matchSegments(pathParts, patternParts, pathIdx + 1, patternIdx + 1);
|
|
2304
2268
|
}
|
|
@@ -2318,12 +2282,10 @@ function matchSegment(text, pattern) {
|
|
|
2318
2282
|
while (patternIdx < pattern.length) {
|
|
2319
2283
|
const char = pattern[patternIdx];
|
|
2320
2284
|
if (char === '*') {
|
|
2321
|
-
// * matches zero or more characters
|
|
2322
2285
|
patternIdx++;
|
|
2323
2286
|
if (patternIdx === pattern.length) {
|
|
2324
|
-
return true;
|
|
2287
|
+
return true;
|
|
2325
2288
|
}
|
|
2326
|
-
// Try matching rest of pattern at each position in text
|
|
2327
2289
|
for (let i = textIdx; i <= text.length; i++) {
|
|
2328
2290
|
if (matchSegmentFrom(text, i, pattern, patternIdx)) {
|
|
2329
2291
|
return true;
|
|
@@ -2332,7 +2294,6 @@ function matchSegment(text, pattern) {
|
|
|
2332
2294
|
return false;
|
|
2333
2295
|
}
|
|
2334
2296
|
else if (char === '?') {
|
|
2335
|
-
// ? matches exactly one character
|
|
2336
2297
|
if (textIdx >= text.length) {
|
|
2337
2298
|
return false;
|
|
2338
2299
|
}
|
|
@@ -2340,10 +2301,8 @@ function matchSegment(text, pattern) {
|
|
|
2340
2301
|
patternIdx++;
|
|
2341
2302
|
}
|
|
2342
2303
|
else if (char === '{') {
|
|
2343
|
-
// {a,b,c} matches any alternative
|
|
2344
2304
|
const closeIdx = findClosingBrace(pattern, patternIdx);
|
|
2345
2305
|
if (closeIdx === -1) {
|
|
2346
|
-
// Unmatched brace, treat as literal
|
|
2347
2306
|
if (textIdx >= text.length || text[textIdx] !== char) {
|
|
2348
2307
|
return false;
|
|
2349
2308
|
}
|
|
@@ -2361,7 +2320,6 @@ function matchSegment(text, pattern) {
|
|
|
2361
2320
|
}
|
|
2362
2321
|
}
|
|
2363
2322
|
else {
|
|
2364
|
-
// Literal character
|
|
2365
2323
|
if (textIdx >= text.length || text[textIdx] !== char) {
|
|
2366
2324
|
return false;
|
|
2367
2325
|
}
|
|
@@ -2787,7 +2745,8 @@ const entryPointLogger = createScopedLogger('project-scope:heuristics:entry-poin
|
|
|
2787
2745
|
*/
|
|
2788
2746
|
const entryPointCache = createCache({ ttl: 60000, maxSize: 50 });
|
|
2789
2747
|
/**
|
|
2790
|
-
* Common entry point patterns.
|
|
2748
|
+
* Common entry point patterns by project type.
|
|
2749
|
+
* Used for convention-based entry point discovery.
|
|
2791
2750
|
*/
|
|
2792
2751
|
const ENTRY_POINT_PATTERNS = {
|
|
2793
2752
|
/** Library entry patterns */
|
|
@@ -3070,7 +3029,6 @@ function collectAllDependencies(packageJson) {
|
|
|
3070
3029
|
function parseVersionString(versionString) {
|
|
3071
3030
|
if (versionString === undefined || versionString === null)
|
|
3072
3031
|
return undefined;
|
|
3073
|
-
// Manual parsing instead of regex to avoid ReDoS
|
|
3074
3032
|
let start = 0;
|
|
3075
3033
|
while (start < versionString.length) {
|
|
3076
3034
|
const char = versionString[start];
|
|
@@ -3131,7 +3089,6 @@ function expressDetector(projectPath, packageJson) {
|
|
|
3131
3089
|
version = parseVersionString(deps['express']);
|
|
3132
3090
|
sources.push({ type: 'package.json', field: 'dependencies.express' });
|
|
3133
3091
|
}
|
|
3134
|
-
// @types/express (indicates usage)
|
|
3135
3092
|
if (deps['@types/express']) {
|
|
3136
3093
|
confidence += 10;
|
|
3137
3094
|
sources.push({ type: 'package.json', field: 'dependencies.@types/express' });
|
|
@@ -3167,13 +3124,11 @@ function nestDetector(projectPath, packageJson) {
|
|
|
3167
3124
|
let version;
|
|
3168
3125
|
let configPath;
|
|
3169
3126
|
const deps = collectAllDependencies(pkg);
|
|
3170
|
-
// @nestjs/core package
|
|
3171
3127
|
if (deps['@nestjs/core']) {
|
|
3172
3128
|
confidence += 70;
|
|
3173
3129
|
version = parseVersionString(deps['@nestjs/core']);
|
|
3174
3130
|
sources.push({ type: 'package.json', field: 'dependencies.@nestjs/core' });
|
|
3175
3131
|
}
|
|
3176
|
-
// @nestjs/common
|
|
3177
3132
|
if (deps['@nestjs/common']) {
|
|
3178
3133
|
confidence += 15;
|
|
3179
3134
|
sources.push({ type: 'package.json', field: 'dependencies.@nestjs/common' });
|
|
@@ -3224,7 +3179,6 @@ function fastifyDetector(projectPath, packageJson) {
|
|
|
3224
3179
|
confidence += 15;
|
|
3225
3180
|
sources.push({ type: 'package.json', field: 'dependencies (fastify plugins)' });
|
|
3226
3181
|
}
|
|
3227
|
-
// @types/fastify (older versions)
|
|
3228
3182
|
if (deps['@types/fastify']) {
|
|
3229
3183
|
confidence += 5;
|
|
3230
3184
|
sources.push({ type: 'package.json', field: 'dependencies.@types/fastify' });
|
|
@@ -3259,7 +3213,6 @@ function koaDetector(projectPath, packageJson) {
|
|
|
3259
3213
|
version = parseVersionString(deps['koa']);
|
|
3260
3214
|
sources.push({ type: 'package.json', field: 'dependencies.koa' });
|
|
3261
3215
|
}
|
|
3262
|
-
// @types/koa
|
|
3263
3216
|
if (deps['@types/koa']) {
|
|
3264
3217
|
confidence += 10;
|
|
3265
3218
|
sources.push({ type: 'package.json', field: 'dependencies.@types/koa' });
|
|
@@ -3838,7 +3791,6 @@ function remixDetector(projectPath, packageJson) {
|
|
|
3838
3791
|
let confidence = 0;
|
|
3839
3792
|
let version;
|
|
3840
3793
|
const deps = collectAllDependencies(pkg);
|
|
3841
|
-
// @remix-run packages
|
|
3842
3794
|
if (deps['@remix-run/react']) {
|
|
3843
3795
|
confidence += 70;
|
|
3844
3796
|
version = parseVersionString(deps['@remix-run/react']);
|
|
@@ -4114,7 +4066,6 @@ function sveltekitDetector(projectPath, packageJson) {
|
|
|
4114
4066
|
let confidence = 0;
|
|
4115
4067
|
let version;
|
|
4116
4068
|
const deps = collectAllDependencies(pkg);
|
|
4117
|
-
// @sveltejs/kit package
|
|
4118
4069
|
if (deps['@sveltejs/kit']) {
|
|
4119
4070
|
confidence += 70;
|
|
4120
4071
|
version = parseVersionString(deps['@sveltejs/kit']);
|
|
@@ -4193,13 +4144,11 @@ function qwikDetector(projectPath, packageJson) {
|
|
|
4193
4144
|
let confidence = 0;
|
|
4194
4145
|
let version;
|
|
4195
4146
|
const deps = collectAllDependencies(pkg);
|
|
4196
|
-
// @builder.io/qwik package
|
|
4197
4147
|
if (deps['@builder.io/qwik']) {
|
|
4198
4148
|
confidence += 70;
|
|
4199
4149
|
version = parseVersionString(deps['@builder.io/qwik']);
|
|
4200
4150
|
sources.push({ type: 'package.json', field: 'dependencies.@builder.io/qwik' });
|
|
4201
4151
|
}
|
|
4202
|
-
// @builder.io/qwik-city
|
|
4203
4152
|
if (deps['@builder.io/qwik-city']) {
|
|
4204
4153
|
confidence += 20;
|
|
4205
4154
|
sources.push({ type: 'package.json', field: 'dependencies.@builder.io/qwik-city' });
|
|
@@ -4310,23 +4259,19 @@ function angularJSDetector(projectPath, packageJson) {
|
|
|
4310
4259
|
let confidence = 0;
|
|
4311
4260
|
let version;
|
|
4312
4261
|
const deps = collectAllDependencies(pkg);
|
|
4313
|
-
// AngularJS package (angular, not @angular/core)
|
|
4314
4262
|
if (deps['angular']) {
|
|
4315
4263
|
confidence += 70;
|
|
4316
4264
|
version = parseVersionString(deps['angular']);
|
|
4317
4265
|
sources.push({ type: 'package.json', field: 'dependencies.angular' });
|
|
4318
4266
|
}
|
|
4319
|
-
// AngularJS router
|
|
4320
4267
|
if (deps['angular-route']) {
|
|
4321
4268
|
confidence += 15;
|
|
4322
4269
|
sources.push({ type: 'package.json', field: 'dependencies.angular-route' });
|
|
4323
4270
|
}
|
|
4324
|
-
// AngularJS resource
|
|
4325
4271
|
if (deps['angular-resource']) {
|
|
4326
4272
|
confidence += 10;
|
|
4327
4273
|
sources.push({ type: 'package.json', field: 'dependencies.angular-resource' });
|
|
4328
4274
|
}
|
|
4329
|
-
// AngularJS animate
|
|
4330
4275
|
if (deps['angular-animate']) {
|
|
4331
4276
|
confidence += 5;
|
|
4332
4277
|
sources.push({ type: 'package.json', field: 'dependencies.angular-animate' });
|
|
@@ -4357,23 +4302,19 @@ function backboneDetector(projectPath, packageJson) {
|
|
|
4357
4302
|
let confidence = 0;
|
|
4358
4303
|
let version;
|
|
4359
4304
|
const deps = collectAllDependencies(pkg);
|
|
4360
|
-
// Backbone package
|
|
4361
4305
|
if (deps['backbone']) {
|
|
4362
4306
|
confidence += 70;
|
|
4363
4307
|
version = parseVersionString(deps['backbone']);
|
|
4364
4308
|
sources.push({ type: 'package.json', field: 'dependencies.backbone' });
|
|
4365
|
-
// Underscore (commonly used with Backbone)
|
|
4366
4309
|
if (deps['underscore']) {
|
|
4367
4310
|
confidence += 15;
|
|
4368
4311
|
sources.push({ type: 'package.json', field: 'dependencies.underscore' });
|
|
4369
4312
|
}
|
|
4370
|
-
// Lodash can be used as underscore replacement
|
|
4371
4313
|
if (deps['lodash']) {
|
|
4372
4314
|
confidence += 5;
|
|
4373
4315
|
sources.push({ type: 'package.json', field: 'dependencies.lodash' });
|
|
4374
4316
|
}
|
|
4375
4317
|
}
|
|
4376
|
-
// Marionette (Backbone framework)
|
|
4377
4318
|
if (deps['backbone.marionette'] || deps['marionette']) {
|
|
4378
4319
|
confidence += 10;
|
|
4379
4320
|
sources.push({ type: 'package.json', field: 'dependencies.backbone.marionette' });
|
|
@@ -4404,18 +4345,15 @@ function emberDetector(projectPath, packageJson) {
|
|
|
4404
4345
|
let confidence = 0;
|
|
4405
4346
|
let version;
|
|
4406
4347
|
const deps = collectAllDependencies(pkg);
|
|
4407
|
-
// Ember source package
|
|
4408
4348
|
if (deps['ember-source']) {
|
|
4409
4349
|
confidence += 70;
|
|
4410
4350
|
version = parseVersionString(deps['ember-source']);
|
|
4411
4351
|
sources.push({ type: 'package.json', field: 'dependencies.ember-source' });
|
|
4412
4352
|
}
|
|
4413
|
-
// Ember CLI
|
|
4414
4353
|
if (deps['ember-cli']) {
|
|
4415
4354
|
confidence += 20;
|
|
4416
4355
|
sources.push({ type: 'package.json', field: 'devDependencies.ember-cli' });
|
|
4417
4356
|
}
|
|
4418
|
-
// Ember Data
|
|
4419
4357
|
if (deps['ember-data']) {
|
|
4420
4358
|
confidence += 10;
|
|
4421
4359
|
sources.push({ type: 'package.json', field: 'dependencies.ember-data' });
|
|
@@ -4446,18 +4384,15 @@ function jqueryDetector(projectPath, packageJson) {
|
|
|
4446
4384
|
let confidence = 0;
|
|
4447
4385
|
let version;
|
|
4448
4386
|
const deps = collectAllDependencies(pkg);
|
|
4449
|
-
// jQuery package
|
|
4450
4387
|
if (deps['jquery']) {
|
|
4451
4388
|
confidence += 80;
|
|
4452
4389
|
version = parseVersionString(deps['jquery']);
|
|
4453
4390
|
sources.push({ type: 'package.json', field: 'dependencies.jquery' });
|
|
4454
4391
|
}
|
|
4455
|
-
// jQuery UI
|
|
4456
4392
|
if (deps['jquery-ui']) {
|
|
4457
4393
|
confidence += 10;
|
|
4458
4394
|
sources.push({ type: 'package.json', field: 'dependencies.jquery-ui' });
|
|
4459
4395
|
}
|
|
4460
|
-
// jQuery plugins
|
|
4461
4396
|
if (deps['jquery-validation']) {
|
|
4462
4397
|
confidence += 5;
|
|
4463
4398
|
sources.push({ type: 'package.json', field: 'dependencies.jquery-validation' });
|
|
@@ -4604,7 +4539,6 @@ function prettierDetector(projectPath, packageJson) {
|
|
|
4604
4539
|
confidence += 30;
|
|
4605
4540
|
sources.push({ type: 'package.json', field: 'prettier' });
|
|
4606
4541
|
}
|
|
4607
|
-
// .prettierignore file
|
|
4608
4542
|
if (exists(join$1(projectPath, '.prettierignore'))) {
|
|
4609
4543
|
confidence += 10;
|
|
4610
4544
|
sources.push({ type: 'config-file', path: '.prettierignore' });
|
|
@@ -4699,7 +4633,6 @@ function biomeDetector(projectPath, packageJson) {
|
|
|
4699
4633
|
let configPath;
|
|
4700
4634
|
let version;
|
|
4701
4635
|
const deps = collectAllDependencies(pkg);
|
|
4702
|
-
// @biomejs/biome package
|
|
4703
4636
|
if (deps['@biomejs/biome']) {
|
|
4704
4637
|
confidence += 70;
|
|
4705
4638
|
version = parseVersionString(deps['@biomejs/biome']);
|
|
@@ -4986,7 +4919,7 @@ function npmWorkspacesDetector(workspacePath, packageJson) {
|
|
|
4986
4919
|
sources.push({ type: 'lockfile', path: 'package-lock.json' });
|
|
4987
4920
|
}
|
|
4988
4921
|
if (exists(join$1(workspacePath, 'yarn.lock'))) {
|
|
4989
|
-
return null;
|
|
4922
|
+
return null;
|
|
4990
4923
|
}
|
|
4991
4924
|
if (confidence === 0) {
|
|
4992
4925
|
return null;
|
|
@@ -5365,7 +5298,6 @@ function checkTsConfigStrict(projectPath) {
|
|
|
5365
5298
|
if (!content)
|
|
5366
5299
|
return undefined;
|
|
5367
5300
|
try {
|
|
5368
|
-
// Simple JSON parsing - doesn't handle comments but good enough for strict check
|
|
5369
5301
|
const cleanContent = content.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, '');
|
|
5370
5302
|
const parsed = parse(cleanContent);
|
|
5371
5303
|
return parsed?.compilerOptions?.strict === true;
|
|
@@ -5388,19 +5320,16 @@ function typescriptDetector(projectPath, packageJson) {
|
|
|
5388
5320
|
let configPath;
|
|
5389
5321
|
let version;
|
|
5390
5322
|
const deps = collectAllDependencies(pkg);
|
|
5391
|
-
// TypeScript package
|
|
5392
5323
|
if (deps['typescript']) {
|
|
5393
5324
|
confidence += 50;
|
|
5394
5325
|
version = parseVersionString(deps['typescript']);
|
|
5395
5326
|
sources.push({ type: 'package.json', field: 'dependencies.typescript' });
|
|
5396
5327
|
}
|
|
5397
|
-
// tsconfig.json
|
|
5398
5328
|
if (exists(join$1(projectPath, 'tsconfig.json'))) {
|
|
5399
5329
|
confidence += 40;
|
|
5400
5330
|
configPath = 'tsconfig.json';
|
|
5401
5331
|
sources.push({ type: 'config-file', path: 'tsconfig.json' });
|
|
5402
5332
|
}
|
|
5403
|
-
// tsconfig.*.json variants
|
|
5404
5333
|
const tsconfigVariants = ['tsconfig.build.json', 'tsconfig.lib.json', 'tsconfig.spec.json', 'tsconfig.app.json'];
|
|
5405
5334
|
for (const variant of tsconfigVariants) {
|
|
5406
5335
|
if (exists(join$1(projectPath, variant))) {
|
|
@@ -5409,7 +5338,6 @@ function typescriptDetector(projectPath, packageJson) {
|
|
|
5409
5338
|
break;
|
|
5410
5339
|
}
|
|
5411
5340
|
}
|
|
5412
|
-
// @types packages
|
|
5413
5341
|
const typePackages = keys(deps).filter((d) => d.startsWith('@types/'));
|
|
5414
5342
|
if (typePackages.length > 0) {
|
|
5415
5343
|
confidence += 10;
|
|
@@ -5443,24 +5371,20 @@ function flowDetector(projectPath, packageJson) {
|
|
|
5443
5371
|
let configPath;
|
|
5444
5372
|
let version;
|
|
5445
5373
|
const deps = collectAllDependencies(pkg);
|
|
5446
|
-
// flow-bin package
|
|
5447
5374
|
if (deps['flow-bin']) {
|
|
5448
5375
|
confidence += 60;
|
|
5449
5376
|
version = parseVersionString(deps['flow-bin']);
|
|
5450
5377
|
sources.push({ type: 'package.json', field: 'dependencies.flow-bin' });
|
|
5451
5378
|
}
|
|
5452
|
-
// .flowconfig
|
|
5453
5379
|
if (exists(join$1(projectPath, '.flowconfig'))) {
|
|
5454
5380
|
confidence += 40;
|
|
5455
5381
|
configPath = '.flowconfig';
|
|
5456
5382
|
sources.push({ type: 'config-file', path: '.flowconfig' });
|
|
5457
5383
|
}
|
|
5458
|
-
// flow-typed directory
|
|
5459
5384
|
if (exists(join$1(projectPath, 'flow-typed'))) {
|
|
5460
5385
|
confidence += 10;
|
|
5461
5386
|
sources.push({ type: 'directory', path: 'flow-typed/' });
|
|
5462
5387
|
}
|
|
5463
|
-
// @babel/preset-flow
|
|
5464
5388
|
if (deps['@babel/preset-flow']) {
|
|
5465
5389
|
confidence += 10;
|
|
5466
5390
|
sources.push({ type: 'package.json', field: 'dependencies.@babel/preset-flow' });
|
|
@@ -5484,7 +5408,6 @@ function flowDetector(projectPath, packageJson) {
|
|
|
5484
5408
|
* @returns `true` if the content contains JSDoc type annotations.
|
|
5485
5409
|
*/
|
|
5486
5410
|
function hasJsDocTypes(content) {
|
|
5487
|
-
// Check for JSDoc type annotations
|
|
5488
5411
|
return (content.includes('@type {') ||
|
|
5489
5412
|
content.includes('@param {') ||
|
|
5490
5413
|
content.includes('@returns {') ||
|
|
@@ -5503,14 +5426,11 @@ function jsdocDetector(projectPath, packageJson) {
|
|
|
5503
5426
|
const sources = [];
|
|
5504
5427
|
let confidence = 0;
|
|
5505
5428
|
const deps = collectAllDependencies(pkg);
|
|
5506
|
-
// jsdoc package
|
|
5507
5429
|
if (deps['jsdoc']) {
|
|
5508
5430
|
confidence += 30;
|
|
5509
5431
|
sources.push({ type: 'package.json', field: 'dependencies.jsdoc' });
|
|
5510
5432
|
}
|
|
5511
|
-
// typescript with checkJs (JSDoc type checking)
|
|
5512
5433
|
if (deps['typescript']) {
|
|
5513
|
-
// Check if checkJs is enabled in tsconfig
|
|
5514
5434
|
const tsconfigPath = join$1(projectPath, 'tsconfig.json');
|
|
5515
5435
|
const content = readFileIfExists(tsconfigPath);
|
|
5516
5436
|
if (content) {
|
|
@@ -5527,12 +5447,10 @@ function jsdocDetector(projectPath, packageJson) {
|
|
|
5527
5447
|
}
|
|
5528
5448
|
}
|
|
5529
5449
|
}
|
|
5530
|
-
// Check for jsconfig.json (VS Code JS type checking)
|
|
5531
5450
|
if (exists(join$1(projectPath, 'jsconfig.json'))) {
|
|
5532
5451
|
confidence += 40;
|
|
5533
5452
|
sources.push({ type: 'config-file', path: 'jsconfig.json' });
|
|
5534
5453
|
}
|
|
5535
|
-
// Sample check for JSDoc annotations in source files
|
|
5536
5454
|
const srcDir = join$1(projectPath, 'src');
|
|
5537
5455
|
if (exists(srcDir)) {
|
|
5538
5456
|
try {
|
|
@@ -5614,8 +5532,6 @@ const allDetectors = {
|
|
|
5614
5532
|
function isDetectAllOptions(value) {
|
|
5615
5533
|
if (typeof value !== 'object' || value === null)
|
|
5616
5534
|
return false;
|
|
5617
|
-
// DetectAllOptions has skipCache or packageJson fields specifically
|
|
5618
|
-
// PackageJson never has skipCache field
|
|
5619
5535
|
return 'skipCache' in value || 'packageJson' in value;
|
|
5620
5536
|
}
|
|
5621
5537
|
/**
|
|
@@ -5647,9 +5563,7 @@ function isDetectAllOptions(value) {
|
|
|
5647
5563
|
* ```
|
|
5648
5564
|
*/
|
|
5649
5565
|
function detectAll(projectPath, packageJsonOrOptions) {
|
|
5650
|
-
// Handle backward-compatible arguments
|
|
5651
5566
|
const options = isDetectAllOptions(packageJsonOrOptions) ? packageJsonOrOptions : { packageJson: packageJsonOrOptions };
|
|
5652
|
-
// Check cache first (unless skipCache is true)
|
|
5653
5567
|
if (!options.skipCache) {
|
|
5654
5568
|
const cached = detectAllCache.get(projectPath);
|
|
5655
5569
|
if (cached) {
|
|
@@ -5700,7 +5614,6 @@ function detectAll(projectPath, packageJsonOrOptions) {
|
|
|
5700
5614
|
legacyFrameworks: result.legacyFrameworks.map((f) => f.id),
|
|
5701
5615
|
testingFrameworks: result.testingFrameworks.map((f) => f.id),
|
|
5702
5616
|
});
|
|
5703
|
-
// Cache the result
|
|
5704
5617
|
detectAllCache.set(projectPath, result);
|
|
5705
5618
|
return result;
|
|
5706
5619
|
}
|
|
@@ -6117,7 +6030,6 @@ function detectNxVersion(workspacePath) {
|
|
|
6117
6030
|
if (packageJson) {
|
|
6118
6031
|
const nxVersion = packageJson.devDependencies?.['nx'] ?? packageJson.dependencies?.['nx'];
|
|
6119
6032
|
if (nxVersion) {
|
|
6120
|
-
// Strip semver range characters (^, ~, >=, etc.)
|
|
6121
6033
|
return nxVersion.replace(/^[\^~>=<]+/, '');
|
|
6122
6034
|
}
|
|
6123
6035
|
}
|
|
@@ -6146,14 +6058,12 @@ function getNxWorkspaceInfo(workspacePath) {
|
|
|
6146
6058
|
}
|
|
6147
6059
|
const nxJson = readJsonFileIfExists(join$1(workspacePath, 'nx.json'));
|
|
6148
6060
|
if (!nxJson) {
|
|
6149
|
-
// Check for workspace.json as fallback (older NX)
|
|
6150
6061
|
const workspaceJson = readJsonFileIfExists(join$1(workspacePath, 'workspace.json'));
|
|
6151
6062
|
if (!workspaceJson) {
|
|
6152
6063
|
nxLogger.debug('No nx.json or workspace.json found', { workspacePath });
|
|
6153
6064
|
return null;
|
|
6154
6065
|
}
|
|
6155
6066
|
nxLogger.debug('Using legacy workspace.json', { workspacePath });
|
|
6156
|
-
// Create minimal nx.json from workspace.json
|
|
6157
6067
|
return {
|
|
6158
6068
|
root: workspacePath,
|
|
6159
6069
|
version: detectNxVersion(workspacePath),
|
|
@@ -6202,7 +6112,6 @@ function tryLoadDevkit() {
|
|
|
6202
6112
|
}
|
|
6203
6113
|
devkitLogger.debug('Attempting to load @nx/devkit');
|
|
6204
6114
|
try {
|
|
6205
|
-
// Dynamic require to avoid bundling
|
|
6206
6115
|
const devkit = require('@nx/devkit');
|
|
6207
6116
|
devkitLogger.debug('@nx/devkit loaded successfully');
|
|
6208
6117
|
cachedResult = { available: true, devkit };
|
|
@@ -6299,7 +6208,6 @@ function readProjectJson(projectPath) {
|
|
|
6299
6208
|
*/
|
|
6300
6209
|
function getProjectConfig(projectPath, workspacePath) {
|
|
6301
6210
|
nxConfigLogger.debug('Getting project config', { projectPath, workspacePath });
|
|
6302
|
-
// Try project.json first
|
|
6303
6211
|
const projectJson = readProjectJson(projectPath);
|
|
6304
6212
|
if (projectJson) {
|
|
6305
6213
|
nxConfigLogger.debug('Using project.json config', { projectPath, name: projectJson.name });
|
|
@@ -6308,7 +6216,6 @@ function getProjectConfig(projectPath, workspacePath) {
|
|
|
6308
6216
|
root: projectJson.root ?? relative(workspacePath, projectPath),
|
|
6309
6217
|
};
|
|
6310
6218
|
}
|
|
6311
|
-
// Try to infer from package.json nx field
|
|
6312
6219
|
const packageJson = readPackageJsonIfExists(projectPath);
|
|
6313
6220
|
if (packageJson && typeof packageJson['nx'] === 'object') {
|
|
6314
6221
|
nxConfigLogger.debug('Using package.json nx field', { projectPath, name: packageJson.name });
|
|
@@ -6337,13 +6244,11 @@ function scanForProjects(dirPath, workspacePath, projects, maxDepth, currentDept
|
|
|
6337
6244
|
try {
|
|
6338
6245
|
const entries = readDirectory(dirPath);
|
|
6339
6246
|
for (const entry of entries) {
|
|
6340
|
-
// Skip node_modules and hidden directories
|
|
6341
6247
|
if (entry.name.startsWith('.') || entry.name === 'node_modules' || entry.name === 'dist') {
|
|
6342
6248
|
continue;
|
|
6343
6249
|
}
|
|
6344
6250
|
const fullPath = join$1(dirPath, entry.name);
|
|
6345
6251
|
if (entry.isDirectory) {
|
|
6346
|
-
// Check if this directory is an NX project
|
|
6347
6252
|
if (isNxProject(fullPath)) {
|
|
6348
6253
|
const config = getProjectConfig(fullPath, workspacePath);
|
|
6349
6254
|
if (config) {
|
|
@@ -6355,7 +6260,6 @@ function scanForProjects(dirPath, workspacePath, projects, maxDepth, currentDept
|
|
|
6355
6260
|
});
|
|
6356
6261
|
}
|
|
6357
6262
|
}
|
|
6358
|
-
// Recursively scan subdirectories
|
|
6359
6263
|
scanForProjects(fullPath, workspacePath, projects, maxDepth, currentDepth + 1);
|
|
6360
6264
|
}
|
|
6361
6265
|
}
|
|
@@ -6373,12 +6277,10 @@ function scanForProjects(dirPath, workspacePath, projects, maxDepth, currentDept
|
|
|
6373
6277
|
*/
|
|
6374
6278
|
function discoverNxProjects(workspacePath) {
|
|
6375
6279
|
const projects = createMap();
|
|
6376
|
-
// Check for workspace.json (older NX format)
|
|
6377
6280
|
const workspaceJson = readJsonFileIfExists(join$1(workspacePath, 'workspace.json'));
|
|
6378
6281
|
if (workspaceJson?.projects) {
|
|
6379
6282
|
for (const [name, config] of entries(workspaceJson.projects)) {
|
|
6380
6283
|
if (typeof config === 'string') {
|
|
6381
|
-
// Path reference to project directory
|
|
6382
6284
|
const projectPath = join$1(workspacePath, config);
|
|
6383
6285
|
const projectConfig = getProjectConfig(projectPath, workspacePath);
|
|
6384
6286
|
if (projectConfig) {
|
|
@@ -6386,18 +6288,15 @@ function discoverNxProjects(workspacePath) {
|
|
|
6386
6288
|
}
|
|
6387
6289
|
}
|
|
6388
6290
|
else if (typeof config === 'object' && config !== null) {
|
|
6389
|
-
// Inline config
|
|
6390
6291
|
projects.set(name, { name, ...config });
|
|
6391
6292
|
}
|
|
6392
6293
|
}
|
|
6393
6294
|
return projects;
|
|
6394
6295
|
}
|
|
6395
|
-
// Scan for project.json files (newer NX format)
|
|
6396
6296
|
const workspaceInfo = getNxWorkspaceInfo(workspacePath);
|
|
6397
6297
|
const appsDir = workspaceInfo?.workspaceLayout.appsDir ?? 'apps';
|
|
6398
6298
|
const libsDir = workspaceInfo?.workspaceLayout.libsDir ?? 'libs';
|
|
6399
6299
|
const searchDirs = [appsDir, libsDir];
|
|
6400
|
-
// Also check packages directory (common in some setups)
|
|
6401
6300
|
if (exists(join$1(workspacePath, 'packages'))) {
|
|
6402
6301
|
searchDirs.push('packages');
|
|
6403
6302
|
}
|
|
@@ -6412,7 +6311,6 @@ function discoverNxProjects(workspacePath) {
|
|
|
6412
6311
|
}
|
|
6413
6312
|
}
|
|
6414
6313
|
}
|
|
6415
|
-
// Also check root-level projects (standalone projects in monorepo root)
|
|
6416
6314
|
if (isNxProject(workspacePath)) {
|
|
6417
6315
|
const config = readProjectJson(workspacePath);
|
|
6418
6316
|
if (config) {
|
|
@@ -6445,10 +6343,8 @@ function buildSimpleProjectGraph(workspacePath, projects) {
|
|
|
6445
6343
|
data: config,
|
|
6446
6344
|
};
|
|
6447
6345
|
dependencies[name] = [];
|
|
6448
|
-
// Add implicit dependencies
|
|
6449
6346
|
if (config.implicitDependencies) {
|
|
6450
6347
|
for (const dep of config.implicitDependencies) {
|
|
6451
|
-
// Skip negative dependencies (those starting with !)
|
|
6452
6348
|
if (!dep.startsWith('!')) {
|
|
6453
6349
|
dependencies[name].push({
|
|
6454
6350
|
target: dep,
|
|
@@ -6465,7 +6361,6 @@ function buildSimpleProjectGraph(workspacePath, projects) {
|
|
|
6465
6361
|
* Known configuration file patterns organized by type.
|
|
6466
6362
|
*/
|
|
6467
6363
|
const CONFIG_PATTERNS = {
|
|
6468
|
-
// Package Management
|
|
6469
6364
|
'package.json': {
|
|
6470
6365
|
patterns: ['package.json'],
|
|
6471
6366
|
format: 'json',
|
|
@@ -6492,14 +6387,12 @@ const CONFIG_PATTERNS = {
|
|
|
6492
6387
|
description: 'NPM configuration',
|
|
6493
6388
|
sensitive: true,
|
|
6494
6389
|
},
|
|
6495
|
-
// TypeScript
|
|
6496
6390
|
tsconfig: {
|
|
6497
6391
|
patterns: ['tsconfig.json', 'tsconfig.*.json'],
|
|
6498
6392
|
format: 'jsonc',
|
|
6499
6393
|
description: 'TypeScript configuration',
|
|
6500
6394
|
canExtend: true,
|
|
6501
6395
|
},
|
|
6502
|
-
// Monorepo
|
|
6503
6396
|
nx: {
|
|
6504
6397
|
patterns: ['nx.json'],
|
|
6505
6398
|
format: 'json',
|
|
@@ -6525,7 +6418,6 @@ const CONFIG_PATTERNS = {
|
|
|
6525
6418
|
format: 'json',
|
|
6526
6419
|
description: 'Lerna configuration',
|
|
6527
6420
|
},
|
|
6528
|
-
// Build Tools
|
|
6529
6421
|
webpack: {
|
|
6530
6422
|
patterns: ['webpack.config.js', 'webpack.config.ts', 'webpack.config.cjs', 'webpack.config.mjs'],
|
|
6531
6423
|
format: 'js',
|
|
@@ -6556,7 +6448,6 @@ const CONFIG_PATTERNS = {
|
|
|
6556
6448
|
format: 'json',
|
|
6557
6449
|
description: 'SWC configuration',
|
|
6558
6450
|
},
|
|
6559
|
-
// Testing
|
|
6560
6451
|
jest: {
|
|
6561
6452
|
patterns: ['jest.config.js', 'jest.config.ts', 'jest.config.mjs'],
|
|
6562
6453
|
description: 'Jest configuration',
|
|
@@ -6573,7 +6464,6 @@ const CONFIG_PATTERNS = {
|
|
|
6573
6464
|
patterns: ['playwright.config.js', 'playwright.config.ts'],
|
|
6574
6465
|
description: 'Playwright configuration',
|
|
6575
6466
|
},
|
|
6576
|
-
// Framework configs
|
|
6577
6467
|
next: {
|
|
6578
6468
|
patterns: ['next.config.js', 'next.config.mjs', 'next.config.ts'],
|
|
6579
6469
|
format: 'js',
|
|
@@ -6599,7 +6489,6 @@ const CONFIG_PATTERNS = {
|
|
|
6599
6489
|
format: 'js',
|
|
6600
6490
|
description: 'Astro configuration',
|
|
6601
6491
|
},
|
|
6602
|
-
// Linting & Formatting
|
|
6603
6492
|
eslint: {
|
|
6604
6493
|
patterns: [
|
|
6605
6494
|
'eslint.config.js',
|
|
@@ -6618,14 +6507,12 @@ const CONFIG_PATTERNS = {
|
|
|
6618
6507
|
format: 'json',
|
|
6619
6508
|
description: 'Prettier configuration',
|
|
6620
6509
|
},
|
|
6621
|
-
// Environment (sensitive)
|
|
6622
6510
|
env: {
|
|
6623
6511
|
patterns: ['.env', '.env.*', '*.env'],
|
|
6624
6512
|
format: 'dotenv',
|
|
6625
6513
|
description: 'Environment variables',
|
|
6626
6514
|
sensitive: true,
|
|
6627
6515
|
},
|
|
6628
|
-
// Git
|
|
6629
6516
|
'.gitignore': {
|
|
6630
6517
|
patterns: ['.gitignore'],
|
|
6631
6518
|
format: 'text',
|
|
@@ -6796,12 +6683,8 @@ function getConfigPaths(type) {
|
|
|
6796
6683
|
*
|
|
6797
6684
|
* @module @hyperfrontend/immutable-api-utils/built-in-copy/number
|
|
6798
6685
|
*/
|
|
6799
|
-
// Capture references at module initialization time
|
|
6800
6686
|
const _parseInt = globalThis.parseInt;
|
|
6801
6687
|
const _parseFloat = globalThis.parseFloat;
|
|
6802
|
-
// ============================================================================
|
|
6803
|
-
// Parsing
|
|
6804
|
-
// ============================================================================
|
|
6805
6688
|
/**
|
|
6806
6689
|
* (Safe copy) Parses a string and returns an integer.
|
|
6807
6690
|
*/
|
|
@@ -7156,11 +7039,9 @@ const analyzeLogger = createScopedLogger('project-scope:analyze');
|
|
|
7156
7039
|
* @returns Detected workspace type
|
|
7157
7040
|
*/
|
|
7158
7041
|
function detectWorkspaceType(projectPath) {
|
|
7159
|
-
// Check for NX
|
|
7160
7042
|
if (isNxWorkspace(projectPath) || findNxWorkspaceRoot(projectPath) !== null) {
|
|
7161
7043
|
return 'nx';
|
|
7162
7044
|
}
|
|
7163
|
-
// Check for common monorepo markers
|
|
7164
7045
|
if (exists(resolve(projectPath, 'turbo.json'))) {
|
|
7165
7046
|
return 'turborepo';
|
|
7166
7047
|
}
|
|
@@ -7170,7 +7051,6 @@ function detectWorkspaceType(projectPath) {
|
|
|
7170
7051
|
if (exists(resolve(projectPath, 'pnpm-workspace.yaml'))) {
|
|
7171
7052
|
return 'pnpm';
|
|
7172
7053
|
}
|
|
7173
|
-
// Check package.json for workspaces
|
|
7174
7054
|
const pkg = readPackageJsonIfExists(projectPath);
|
|
7175
7055
|
if (pkg?.workspaces) {
|
|
7176
7056
|
if (exists(resolve(projectPath, 'yarn.lock'))) {
|
|
@@ -7179,7 +7059,7 @@ function detectWorkspaceType(projectPath) {
|
|
|
7179
7059
|
if (exists(resolve(projectPath, 'package-lock.json'))) {
|
|
7180
7060
|
return 'npm';
|
|
7181
7061
|
}
|
|
7182
|
-
return 'npm';
|
|
7062
|
+
return 'npm';
|
|
7183
7063
|
}
|
|
7184
7064
|
return 'standalone';
|
|
7185
7065
|
}
|
|
@@ -7191,15 +7071,12 @@ function detectWorkspaceType(projectPath) {
|
|
|
7191
7071
|
* @returns True if the analysis should be performed
|
|
7192
7072
|
*/
|
|
7193
7073
|
function shouldInclude(type, options) {
|
|
7194
|
-
// If include list is specified, item must be in it
|
|
7195
7074
|
if (options?.include && options.include.length > 0) {
|
|
7196
7075
|
return options.include.includes(type);
|
|
7197
7076
|
}
|
|
7198
|
-
// If exclude list is specified, item must not be in it
|
|
7199
7077
|
if (options?.exclude && options.exclude.length > 0) {
|
|
7200
7078
|
return !options.exclude.includes(type);
|
|
7201
7079
|
}
|
|
7202
|
-
// Default: include everything
|
|
7203
7080
|
return true;
|
|
7204
7081
|
}
|
|
7205
7082
|
/**
|
|
@@ -7248,28 +7125,22 @@ function normalizeConfigFormat(format) {
|
|
|
7248
7125
|
function analyzeProject(projectPath, options) {
|
|
7249
7126
|
const startTime = dateNow();
|
|
7250
7127
|
const resolvedPath = resolve(projectPath);
|
|
7251
|
-
// Set log level based on verbose option
|
|
7252
7128
|
if (options?.verbose) {
|
|
7253
7129
|
analyzeLogger.setLogLevel('debug');
|
|
7254
7130
|
}
|
|
7255
7131
|
analyzeLogger.debug('Starting project analysis', { path: resolvedPath, options });
|
|
7256
|
-
// Read package.json once for reuse
|
|
7257
7132
|
const packageJson = readPackageJsonIfExists(resolvedPath);
|
|
7258
7133
|
analyzeLogger.debug('Package.json loaded', { found: !!packageJson, name: packageJson?.name });
|
|
7259
|
-
// Detect project and workspace types
|
|
7260
7134
|
const projectTypeDetection = detectProjectType(resolvedPath, {
|
|
7261
7135
|
skipTechDetection: options?.depth === 'basic',
|
|
7262
7136
|
});
|
|
7263
7137
|
const projectType = projectTypeDetection.type;
|
|
7264
7138
|
const workspaceType = detectWorkspaceType(resolvedPath);
|
|
7265
7139
|
analyzeLogger.debug('Project type detected', { projectType, workspaceType });
|
|
7266
|
-
// Get project name from package.json or directory name
|
|
7267
7140
|
const projectName = packageJson?.name ?? basename(resolvedPath);
|
|
7268
|
-
// Run all technology detectors
|
|
7269
7141
|
const detections = shouldInclude('frameworks', options) || shouldInclude('buildTools', options) || shouldInclude('testing', options)
|
|
7270
7142
|
? detectAll(resolvedPath, packageJson ?? undefined)
|
|
7271
7143
|
: null;
|
|
7272
|
-
// Convert framework detections to FrameworkInfo
|
|
7273
7144
|
const frameworks = shouldInclude('frameworks', options) && detections
|
|
7274
7145
|
? [
|
|
7275
7146
|
...detections.frontendFrameworks.map((d) => ({
|
|
@@ -7289,7 +7160,6 @@ function analyzeProject(projectPath, options) {
|
|
|
7289
7160
|
})),
|
|
7290
7161
|
]
|
|
7291
7162
|
: [];
|
|
7292
|
-
// Convert build tool detections
|
|
7293
7163
|
const buildTools = shouldInclude('buildTools', options) && detections
|
|
7294
7164
|
? detections.buildTools.map((d) => ({
|
|
7295
7165
|
id: d.id,
|
|
@@ -7299,7 +7169,6 @@ function analyzeProject(projectPath, options) {
|
|
|
7299
7169
|
confidence: d.confidence,
|
|
7300
7170
|
}))
|
|
7301
7171
|
: [];
|
|
7302
|
-
// Convert testing framework detections
|
|
7303
7172
|
const testingFrameworks = shouldInclude('testing', options) && detections
|
|
7304
7173
|
? detections.testingFrameworks.map((d) => ({
|
|
7305
7174
|
id: d.id,
|
|
@@ -7310,7 +7179,6 @@ function analyzeProject(projectPath, options) {
|
|
|
7310
7179
|
confidence: d.confidence,
|
|
7311
7180
|
}))
|
|
7312
7181
|
: [];
|
|
7313
|
-
// Discover entry points
|
|
7314
7182
|
const entryPoints = shouldInclude('entryPoints', options)
|
|
7315
7183
|
? discoverEntryPoints(resolvedPath).map((e) => ({
|
|
7316
7184
|
path: e.path,
|
|
@@ -7318,7 +7186,6 @@ function analyzeProject(projectPath, options) {
|
|
|
7318
7186
|
confidence: e.confidence,
|
|
7319
7187
|
}))
|
|
7320
7188
|
: [];
|
|
7321
|
-
// Detect configuration files
|
|
7322
7189
|
const configFiles = shouldInclude('configs', options)
|
|
7323
7190
|
? detectConfigs(resolvedPath).map((c) => ({
|
|
7324
7191
|
path: c.path,
|
|
@@ -7327,7 +7194,6 @@ function analyzeProject(projectPath, options) {
|
|
|
7327
7194
|
tool: c.type,
|
|
7328
7195
|
}))
|
|
7329
7196
|
: [];
|
|
7330
|
-
// Get dependency summary
|
|
7331
7197
|
let dependencies;
|
|
7332
7198
|
if (shouldInclude('dependencies', options)) {
|
|
7333
7199
|
const deps = getProjectDependencies(resolvedPath);
|
|
@@ -7348,7 +7214,6 @@ function analyzeProject(projectPath, options) {
|
|
|
7348
7214
|
total: 0,
|
|
7349
7215
|
};
|
|
7350
7216
|
}
|
|
7351
|
-
// Build metadata
|
|
7352
7217
|
const metadata = {
|
|
7353
7218
|
timestamp: createDate(),
|
|
7354
7219
|
durationMs: dateNow() - startTime,
|
|
@@ -7423,15 +7288,12 @@ function formatWorkspaceType(type) {
|
|
|
7423
7288
|
*/
|
|
7424
7289
|
function formatAnalysisText(result) {
|
|
7425
7290
|
const lines = [];
|
|
7426
|
-
// Header
|
|
7427
7291
|
lines.push(`Project Analysis: ${result.name}`);
|
|
7428
7292
|
lines.push('='.repeat(30));
|
|
7429
7293
|
lines.push('');
|
|
7430
|
-
// Basic info
|
|
7431
7294
|
lines.push(`Type: ${formatProjectType(result.projectType)}`);
|
|
7432
7295
|
lines.push(`Workspace: ${formatWorkspaceType(result.workspaceType)}`);
|
|
7433
7296
|
lines.push('');
|
|
7434
|
-
// Frameworks
|
|
7435
7297
|
if (result.frameworks.length > 0) {
|
|
7436
7298
|
lines.push('Frameworks:');
|
|
7437
7299
|
for (const framework of result.frameworks) {
|
|
@@ -7445,7 +7307,6 @@ function formatAnalysisText(result) {
|
|
|
7445
7307
|
}
|
|
7446
7308
|
lines.push('');
|
|
7447
7309
|
}
|
|
7448
|
-
// Build tools
|
|
7449
7310
|
if (result.buildTools.length > 0) {
|
|
7450
7311
|
lines.push('Build Tools:');
|
|
7451
7312
|
for (const tool of result.buildTools) {
|
|
@@ -7454,7 +7315,6 @@ function formatAnalysisText(result) {
|
|
|
7454
7315
|
}
|
|
7455
7316
|
lines.push('');
|
|
7456
7317
|
}
|
|
7457
|
-
// Testing
|
|
7458
7318
|
if (result.testingFrameworks.length > 0) {
|
|
7459
7319
|
lines.push('Testing:');
|
|
7460
7320
|
for (const framework of result.testingFrameworks) {
|
|
@@ -7463,7 +7323,6 @@ function formatAnalysisText(result) {
|
|
|
7463
7323
|
}
|
|
7464
7324
|
lines.push('');
|
|
7465
7325
|
}
|
|
7466
|
-
// Entry points
|
|
7467
7326
|
if (result.entryPoints.length > 0) {
|
|
7468
7327
|
lines.push('Entry Points:');
|
|
7469
7328
|
for (const entry of result.entryPoints.slice(0, 5)) {
|
|
@@ -7474,7 +7333,6 @@ function formatAnalysisText(result) {
|
|
|
7474
7333
|
}
|
|
7475
7334
|
lines.push('');
|
|
7476
7335
|
}
|
|
7477
|
-
// Configurations
|
|
7478
7336
|
if (result.configFiles.length > 0) {
|
|
7479
7337
|
lines.push('Configurations:');
|
|
7480
7338
|
for (const config of result.configFiles.slice(0, 8)) {
|
|
@@ -7485,7 +7343,6 @@ function formatAnalysisText(result) {
|
|
|
7485
7343
|
}
|
|
7486
7344
|
lines.push('');
|
|
7487
7345
|
}
|
|
7488
|
-
// Dependencies summary
|
|
7489
7346
|
lines.push('Dependencies:');
|
|
7490
7347
|
lines.push(` Production: ${result.dependencies.production}`);
|
|
7491
7348
|
lines.push(` Development: ${result.dependencies.development}`);
|
|
@@ -7572,7 +7429,7 @@ function parseAnalyzeArgs(args) {
|
|
|
7572
7429
|
exclude: { type: 'string', short: 'e' },
|
|
7573
7430
|
},
|
|
7574
7431
|
allowPositionals: true,
|
|
7575
|
-
strict: false,
|
|
7432
|
+
strict: false,
|
|
7576
7433
|
});
|
|
7577
7434
|
const format = values.format;
|
|
7578
7435
|
const depth = values.depth;
|
|
@@ -7631,7 +7488,6 @@ const analyzeCommandDef = {
|
|
|
7631
7488
|
description: 'Analyze project structure and tech stack',
|
|
7632
7489
|
execute(args, globalOptions) {
|
|
7633
7490
|
const options = parseAnalyzeArgs(args);
|
|
7634
|
-
// Global --json flag overrides format
|
|
7635
7491
|
if (globalOptions.json) {
|
|
7636
7492
|
options.format = 'json';
|
|
7637
7493
|
}
|
|
@@ -7938,12 +7794,10 @@ Examples:
|
|
|
7938
7794
|
function formatDependencyList(deps, maxItems = 20) {
|
|
7939
7795
|
const lines = [];
|
|
7940
7796
|
const depEntries = entries(deps);
|
|
7941
|
-
// Sort alphabetically
|
|
7942
7797
|
depEntries.sort((a, b) => a[0].localeCompare(b[0]));
|
|
7943
7798
|
const displayCount = min(depEntries.length, maxItems);
|
|
7944
7799
|
for (let i = 0; i < displayCount; i++) {
|
|
7945
7800
|
const [name, version] = depEntries[i];
|
|
7946
|
-
// Pad name to align versions
|
|
7947
7801
|
const paddedName = name.padEnd(30);
|
|
7948
7802
|
lines.push(` ${paddedName} ${version}`);
|
|
7949
7803
|
}
|
|
@@ -8186,13 +8040,10 @@ function buildTree(rootPath, walkEntries, options) {
|
|
|
8186
8040
|
isDirectory: true,
|
|
8187
8041
|
children: [],
|
|
8188
8042
|
};
|
|
8189
|
-
// Create a map for quick lookup
|
|
8190
8043
|
const nodeMap = createMap();
|
|
8191
8044
|
nodeMap.set('.', root);
|
|
8192
|
-
// Sort entries by path for proper parent-child relationships
|
|
8193
8045
|
const sortedEntries = [...walkEntries].sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
8194
8046
|
for (const entry of sortedEntries) {
|
|
8195
|
-
// Skip based on filters
|
|
8196
8047
|
if (options.dirsOnly && !entry.isDirectory)
|
|
8197
8048
|
continue;
|
|
8198
8049
|
if (options.filesOnly && entry.isDirectory)
|
|
@@ -8203,7 +8054,6 @@ function buildTree(rootPath, walkEntries, options) {
|
|
|
8203
8054
|
isDirectory: entry.isDirectory,
|
|
8204
8055
|
children: [],
|
|
8205
8056
|
};
|
|
8206
|
-
// Add size/modified if requested
|
|
8207
8057
|
if ((options.showSize || options.showModified) && entry.isFile) {
|
|
8208
8058
|
const stats = getFileStat(entry.path);
|
|
8209
8059
|
if (stats) {
|
|
@@ -8213,9 +8063,8 @@ function buildTree(rootPath, walkEntries, options) {
|
|
|
8213
8063
|
node.modified = stats.modified;
|
|
8214
8064
|
}
|
|
8215
8065
|
}
|
|
8216
|
-
// Find parent
|
|
8217
8066
|
const parts = entry.relativePath.split('/');
|
|
8218
|
-
parts.pop();
|
|
8067
|
+
parts.pop();
|
|
8219
8068
|
const parentPath = parts.join('/') || '.';
|
|
8220
8069
|
const parent = nodeMap.get(parentPath);
|
|
8221
8070
|
if (parent) {
|
|
@@ -8236,11 +8085,9 @@ function buildTree(rootPath, walkEntries, options) {
|
|
|
8236
8085
|
*/
|
|
8237
8086
|
function renderTreeText(node, options, prefix = '', isLast = true) {
|
|
8238
8087
|
const lines = [];
|
|
8239
|
-
// Current line
|
|
8240
8088
|
const connector = isLast ? '└── ' : '├── ';
|
|
8241
8089
|
const dirMark = node.isDirectory ? '/' : '';
|
|
8242
8090
|
let line = `${prefix}${connector}${node.name}${dirMark}`;
|
|
8243
|
-
// Add size/modified
|
|
8244
8091
|
const meta = [];
|
|
8245
8092
|
if (options.showSize && node.size !== undefined) {
|
|
8246
8093
|
meta.push(formatSize(node.size));
|
|
@@ -8252,10 +8099,8 @@ function renderTreeText(node, options, prefix = '', isLast = true) {
|
|
|
8252
8099
|
line += ` [${meta.join(' ')}]`;
|
|
8253
8100
|
}
|
|
8254
8101
|
lines.push(line);
|
|
8255
|
-
// Process children
|
|
8256
8102
|
const childPrefix = prefix + (isLast ? ' ' : '│ ');
|
|
8257
8103
|
const sortedChildren = [...node.children].sort((a, b) => {
|
|
8258
|
-
// Directories first, then alphabetically
|
|
8259
8104
|
if (a.isDirectory && !b.isDirectory)
|
|
8260
8105
|
return -1;
|
|
8261
8106
|
if (!a.isDirectory && b.isDirectory)
|
|
@@ -8279,9 +8124,7 @@ function renderTreeText(node, options, prefix = '', isLast = true) {
|
|
|
8279
8124
|
*/
|
|
8280
8125
|
function formatTreeText(rootPath, tree, options) {
|
|
8281
8126
|
const lines = [];
|
|
8282
|
-
// Root line
|
|
8283
8127
|
lines.push(basename(rootPath));
|
|
8284
|
-
// Count stats
|
|
8285
8128
|
let dirCount = 0;
|
|
8286
8129
|
let fileCount = 0;
|
|
8287
8130
|
/**
|
|
@@ -8300,7 +8143,6 @@ function formatTreeText(rootPath, tree, options) {
|
|
|
8300
8143
|
countNodes(child);
|
|
8301
8144
|
}
|
|
8302
8145
|
}
|
|
8303
|
-
// Render children of root
|
|
8304
8146
|
const sortedChildren = [...tree.children].sort((a, b) => {
|
|
8305
8147
|
if (a.isDirectory && !b.isDirectory)
|
|
8306
8148
|
return -1;
|
|
@@ -8314,7 +8156,6 @@ function formatTreeText(rootPath, tree, options) {
|
|
|
8314
8156
|
lines.push(...renderTreeText(child, options, '', isLast));
|
|
8315
8157
|
countNodes(child);
|
|
8316
8158
|
}
|
|
8317
|
-
// Summary
|
|
8318
8159
|
lines.push('');
|
|
8319
8160
|
const dirText = dirCount === 1 ? '1 directory' : `${dirCount} directories`;
|
|
8320
8161
|
const fileText = fileCount === 1 ? '1 file' : `${fileCount} files`;
|
|
@@ -8375,7 +8216,6 @@ function parseTreeArgs(args) {
|
|
|
8375
8216
|
function treeCommand(options) {
|
|
8376
8217
|
const rootPath = options.path ? resolve(options.path) : process.cwd();
|
|
8377
8218
|
try {
|
|
8378
|
-
// Collect entries via walk
|
|
8379
8219
|
const walkEntries = [];
|
|
8380
8220
|
walkDirectory(rootPath, (entry) => {
|
|
8381
8221
|
walkEntries.push(entry);
|
|
@@ -8385,9 +8225,7 @@ function treeCommand(options) {
|
|
|
8385
8225
|
ignorePatterns: options.ignore,
|
|
8386
8226
|
includeHidden: false,
|
|
8387
8227
|
});
|
|
8388
|
-
// Build tree structure
|
|
8389
8228
|
const tree = buildTree(rootPath, walkEntries, options);
|
|
8390
|
-
// Format output
|
|
8391
8229
|
let output;
|
|
8392
8230
|
if (options.format === 'json') {
|
|
8393
8231
|
output = formatTreeJson(tree);
|
|
@@ -8446,6 +8284,9 @@ Examples:
|
|
|
8446
8284
|
|
|
8447
8285
|
/** Logger for CLI operations */
|
|
8448
8286
|
const cliLogger = createScopedLogger('project-scope:cli');
|
|
8287
|
+
/** Output printer for user-facing CLI output (help, version, command results). */
|
|
8288
|
+
const output = createLogger(error, undefined, log);
|
|
8289
|
+
output.setLogLevel('log');
|
|
8449
8290
|
/** Library version */
|
|
8450
8291
|
const VERSION = '0.1.0';
|
|
8451
8292
|
/**
|
|
@@ -8461,7 +8302,7 @@ const commands = {
|
|
|
8461
8302
|
* Print general help information.
|
|
8462
8303
|
*/
|
|
8463
8304
|
function printHelp() {
|
|
8464
|
-
log(`
|
|
8305
|
+
output.log(`
|
|
8465
8306
|
project-scope <command> [options]
|
|
8466
8307
|
|
|
8467
8308
|
A tool for analyzing JavaScript/TypeScript project structure and tech stack.
|
|
@@ -8493,7 +8334,7 @@ Examples:
|
|
|
8493
8334
|
* Print CLI version.
|
|
8494
8335
|
*/
|
|
8495
8336
|
function printVersion() {
|
|
8496
|
-
log(`project-scope v${VERSION}`);
|
|
8337
|
+
output.log(`project-scope v${VERSION}`);
|
|
8497
8338
|
}
|
|
8498
8339
|
/**
|
|
8499
8340
|
* Parse global options from command line arguments.
|
|
@@ -8554,24 +8395,24 @@ function run(args) {
|
|
|
8554
8395
|
setGlobalLogLevel('debug');
|
|
8555
8396
|
}
|
|
8556
8397
|
cliLogger.debug('CLI invoked', { args, globalOptions });
|
|
8398
|
+
const commandName = args[0];
|
|
8557
8399
|
if (globalOptions.version) {
|
|
8558
8400
|
printVersion();
|
|
8559
8401
|
return { exitCode: 0 };
|
|
8560
8402
|
}
|
|
8561
|
-
if (globalOptions.help && (args.length === 1 || !commands[
|
|
8403
|
+
if (globalOptions.help && (args.length === 1 || !commands[commandName])) {
|
|
8562
8404
|
printHelp();
|
|
8563
8405
|
return { exitCode: 0 };
|
|
8564
8406
|
}
|
|
8565
|
-
const commandName = args[0];
|
|
8566
8407
|
const command = commands[commandName];
|
|
8567
8408
|
if (!command) {
|
|
8568
8409
|
cliLogger.warn('Unknown command requested', { commandName });
|
|
8569
|
-
error(`Unknown command: ${commandName}`);
|
|
8570
|
-
error('Run "project-scope --help" for usage information.');
|
|
8410
|
+
output.error(`Unknown command: ${commandName}`);
|
|
8411
|
+
output.error('Run "project-scope --help" for usage information.');
|
|
8571
8412
|
return { exitCode: 1, error: `Unknown command: ${commandName}` };
|
|
8572
8413
|
}
|
|
8573
8414
|
if (globalOptions.help) {
|
|
8574
|
-
log(command.getHelp());
|
|
8415
|
+
output.log(command.getHelp());
|
|
8575
8416
|
return { exitCode: 0 };
|
|
8576
8417
|
}
|
|
8577
8418
|
const commandArgs = args.slice(1);
|
|
@@ -8579,11 +8420,11 @@ function run(args) {
|
|
|
8579
8420
|
const result = command.execute(commandArgs, globalOptions);
|
|
8580
8421
|
cliLogger.debug('Command completed', { commandName, exitCode: result.exitCode });
|
|
8581
8422
|
if (result.output) {
|
|
8582
|
-
log(result.output);
|
|
8423
|
+
output.log(result.output);
|
|
8583
8424
|
}
|
|
8584
8425
|
if (result.error) {
|
|
8585
8426
|
cliLogger.error('Command error', { commandName, error: result.error });
|
|
8586
|
-
error(result.error);
|
|
8427
|
+
output.error(result.error);
|
|
8587
8428
|
}
|
|
8588
8429
|
return result;
|
|
8589
8430
|
}
|
|
@@ -8619,25 +8460,22 @@ const BINARY_SIGNATURES = [
|
|
|
8619
8460
|
*/
|
|
8620
8461
|
function detectEncodingInfo(buffer) {
|
|
8621
8462
|
encodingLogger.debug('Detecting encoding info', { bufferSize: buffer.length });
|
|
8622
|
-
// Check for UTF-8 BOM
|
|
8623
8463
|
if (buffer.length >= 3) {
|
|
8624
8464
|
if (buffer[0] === UTF8_BOM_BYTES[0] && buffer[1] === UTF8_BOM_BYTES[1] && buffer[2] === UTF8_BOM_BYTES[2]) {
|
|
8625
8465
|
encodingLogger.debug('Detected UTF-8 BOM');
|
|
8626
8466
|
return { type: 'text', encoding: 'utf-8', hasBom: true };
|
|
8627
8467
|
}
|
|
8628
8468
|
}
|
|
8629
|
-
// Check for UTF-16 BOMs
|
|
8630
8469
|
if (buffer.length >= 2) {
|
|
8631
8470
|
if (buffer[0] === UTF16_BE_BOM_BYTES[0] && buffer[1] === UTF16_BE_BOM_BYTES[1]) {
|
|
8632
8471
|
encodingLogger.debug('Detected UTF-16 BE BOM');
|
|
8633
|
-
return { type: 'text', encoding: 'utf16le', hasBom: true };
|
|
8472
|
+
return { type: 'text', encoding: 'utf16le', hasBom: true };
|
|
8634
8473
|
}
|
|
8635
8474
|
if (buffer[0] === UTF16_LE_BOM_BYTES[0] && buffer[1] === UTF16_LE_BOM_BYTES[1]) {
|
|
8636
8475
|
encodingLogger.debug('Detected UTF-16 LE BOM');
|
|
8637
8476
|
return { type: 'text', encoding: 'utf16le', hasBom: true };
|
|
8638
8477
|
}
|
|
8639
8478
|
}
|
|
8640
|
-
// Check for binary signatures
|
|
8641
8479
|
for (const { signature, description } of BINARY_SIGNATURES) {
|
|
8642
8480
|
if (buffer.length >= signature.length) {
|
|
8643
8481
|
let matches = true;
|
|
@@ -8653,7 +8491,6 @@ function detectEncodingInfo(buffer) {
|
|
|
8653
8491
|
}
|
|
8654
8492
|
}
|
|
8655
8493
|
}
|
|
8656
|
-
// Check for null bytes (usually indicates binary)
|
|
8657
8494
|
const sampleSize = min(buffer.length, 8000);
|
|
8658
8495
|
for (let i = 0; i < sampleSize; i++) {
|
|
8659
8496
|
if (buffer[i] === 0) {
|
|
@@ -8672,22 +8509,18 @@ function detectEncodingInfo(buffer) {
|
|
|
8672
8509
|
*/
|
|
8673
8510
|
function detectEncoding(buffer) {
|
|
8674
8511
|
if (buffer.length >= 3) {
|
|
8675
|
-
// Check for UTF-8 BOM
|
|
8676
8512
|
if (buffer[0] === UTF8_BOM_BYTES[0] && buffer[1] === UTF8_BOM_BYTES[1] && buffer[2] === UTF8_BOM_BYTES[2]) {
|
|
8677
8513
|
return 'utf-8';
|
|
8678
8514
|
}
|
|
8679
8515
|
}
|
|
8680
8516
|
if (buffer.length >= 2) {
|
|
8681
|
-
// Check for UTF-16 LE BOM
|
|
8682
8517
|
if (buffer[0] === UTF16_LE_BOM_BYTES[0] && buffer[1] === UTF16_LE_BOM_BYTES[1]) {
|
|
8683
8518
|
return 'utf16le';
|
|
8684
8519
|
}
|
|
8685
|
-
// Check for UTF-16 BE BOM
|
|
8686
8520
|
if (buffer[0] === UTF16_BE_BOM_BYTES[0] && buffer[1] === UTF16_BE_BOM_BYTES[1]) {
|
|
8687
|
-
return 'utf16le';
|
|
8521
|
+
return 'utf16le';
|
|
8688
8522
|
}
|
|
8689
8523
|
}
|
|
8690
|
-
// Default to UTF-8
|
|
8691
8524
|
return 'utf-8';
|
|
8692
8525
|
}
|
|
8693
8526
|
/**
|
|
@@ -8757,10 +8590,8 @@ function bufferToString(content, encoding) {
|
|
|
8757
8590
|
convertLogger.debug('Using provided encoding', { encoding });
|
|
8758
8591
|
return content.toString(encoding);
|
|
8759
8592
|
}
|
|
8760
|
-
// Auto-detect and convert
|
|
8761
8593
|
const info = detectEncodingInfo(content);
|
|
8762
8594
|
if (info.type === 'text') {
|
|
8763
|
-
// Remove BOM if present
|
|
8764
8595
|
let offset = 0;
|
|
8765
8596
|
if (info.hasBom) {
|
|
8766
8597
|
offset = info.encoding === 'utf-8' ? 3 : 2;
|
|
@@ -8813,19 +8644,14 @@ function detectCaseSensitivity() {
|
|
|
8813
8644
|
if (cachedCaseSensitive !== null) {
|
|
8814
8645
|
return cachedCaseSensitive;
|
|
8815
8646
|
}
|
|
8816
|
-
// Quick check based on platform
|
|
8817
8647
|
if (process.platform === 'win32') {
|
|
8818
8648
|
cachedCaseSensitive = false;
|
|
8819
8649
|
return false;
|
|
8820
8650
|
}
|
|
8821
|
-
// macOS is typically case-insensitive by default
|
|
8822
8651
|
if (process.platform === 'darwin') {
|
|
8823
|
-
// Could be case-sensitive HFS+/APFS, but assume insensitive by default
|
|
8824
8652
|
cachedCaseSensitive = false;
|
|
8825
8653
|
return false;
|
|
8826
8654
|
}
|
|
8827
|
-
// Test actual file system behavior for Linux and others
|
|
8828
|
-
// Use mkdtempSync to create a secure temporary directory
|
|
8829
8655
|
let secureTestDir = null;
|
|
8830
8656
|
try {
|
|
8831
8657
|
secureTestDir = mkdtempSync(join$1(tmpdir(), 'case-sensitivity-test-'));
|
|
@@ -8836,12 +8662,9 @@ function detectCaseSensitivity() {
|
|
|
8836
8662
|
unlinkSync(testFile);
|
|
8837
8663
|
}
|
|
8838
8664
|
catch {
|
|
8839
|
-
// Default to case-sensitive on Linux/Unix if test fails
|
|
8840
|
-
// (win32 and darwin already returned early, so we're on a case-sensitive platform)
|
|
8841
8665
|
cachedCaseSensitive = true;
|
|
8842
8666
|
}
|
|
8843
8667
|
finally {
|
|
8844
|
-
// Clean up the secure temporary directory
|
|
8845
8668
|
if (secureTestDir) {
|
|
8846
8669
|
try {
|
|
8847
8670
|
rmdirSync(secureTestDir);
|
|
@@ -8976,7 +8799,6 @@ function normalizeLineEndings(content, style = 'lf') {
|
|
|
8976
8799
|
else {
|
|
8977
8800
|
target = style === 'crlf' ? CRLF : LF;
|
|
8978
8801
|
}
|
|
8979
|
-
// First normalize all to LF, then convert to target
|
|
8980
8802
|
const normalized = content.replace(/\r\n/g, LF).replace(/\r/g, LF);
|
|
8981
8803
|
if (target === LF) {
|
|
8982
8804
|
return normalized;
|
|
@@ -8991,7 +8813,6 @@ function normalizeLineEndings(content, style = 'lf') {
|
|
|
8991
8813
|
*/
|
|
8992
8814
|
function detectLineEnding(content) {
|
|
8993
8815
|
const hasCRLF = content.includes(CRLF);
|
|
8994
|
-
// Check for LF that is NOT part of CRLF
|
|
8995
8816
|
const hasLFOnly = content.includes('\n') && content.replace(/\r\n/g, '').includes('\n');
|
|
8996
8817
|
if (hasCRLF && hasLFOnly)
|
|
8997
8818
|
return 'mixed';
|
|
@@ -9469,7 +9290,6 @@ function createFsTree(root, options) {
|
|
|
9469
9290
|
}
|
|
9470
9291
|
const prefix = normalPath === '.' || normalPath === '' ? '' : normalPath + '/';
|
|
9471
9292
|
for (const [changedPath, change] of _changes) {
|
|
9472
|
-
// Handle root-level files
|
|
9473
9293
|
if (prefix === '') {
|
|
9474
9294
|
const childName = changedPath.split('/')[0];
|
|
9475
9295
|
if (change.type === 'DELETE' && !changedPath.includes('/')) {
|
|
@@ -9486,7 +9306,6 @@ function createFsTree(root, options) {
|
|
|
9486
9306
|
const relativePath = changedPath.slice(prefix.length);
|
|
9487
9307
|
const childName = relativePath.split('/')[0];
|
|
9488
9308
|
if (change.type === 'DELETE') {
|
|
9489
|
-
// Only remove if it's a direct child being deleted
|
|
9490
9309
|
if (!relativePath.includes('/')) {
|
|
9491
9310
|
childSet.delete(childName);
|
|
9492
9311
|
}
|
|
@@ -9572,12 +9391,10 @@ const factoryLogger = createScopedLogger('project-scope:vfs:factory');
|
|
|
9572
9391
|
function createTree(root, options) {
|
|
9573
9392
|
const normalizedRoot = normalizePath(root);
|
|
9574
9393
|
factoryLogger.debug('createTree', { root: normalizedRoot });
|
|
9575
|
-
// Validate root exists
|
|
9576
9394
|
if (!exists(normalizedRoot)) {
|
|
9577
9395
|
factoryLogger.warn('createTree failed: root does not exist', { root: normalizedRoot });
|
|
9578
9396
|
throw createError(`Root directory does not exist: ${normalizedRoot}`);
|
|
9579
9397
|
}
|
|
9580
|
-
// Validate root is a directory
|
|
9581
9398
|
if (!isDirectory(normalizedRoot)) {
|
|
9582
9399
|
factoryLogger.warn('createTree failed: root is not a directory', { root: normalizedRoot });
|
|
9583
9400
|
throw createError(`Root path is not a directory: ${normalizedRoot}`);
|
|
@@ -9624,7 +9441,6 @@ const vfsLogger = createScopedLogger('project-scope:vfs');
|
|
|
9624
9441
|
function commitChanges(tree, options) {
|
|
9625
9442
|
const changes = tree.listChanges();
|
|
9626
9443
|
const appliedChanges = [];
|
|
9627
|
-
// Set log level based on verbose option
|
|
9628
9444
|
if (options?.verbose) {
|
|
9629
9445
|
vfsLogger.setLogLevel('debug');
|
|
9630
9446
|
}
|
|
@@ -9636,7 +9452,6 @@ function commitChanges(tree, options) {
|
|
|
9636
9452
|
changes: [],
|
|
9637
9453
|
dryRun: options?.dryRun ?? false,
|
|
9638
9454
|
};
|
|
9639
|
-
// Dry run - just count changes without writing
|
|
9640
9455
|
if (options?.dryRun) {
|
|
9641
9456
|
for (const change of changes) {
|
|
9642
9457
|
switch (change.type) {
|
|
@@ -9654,7 +9469,6 @@ function commitChanges(tree, options) {
|
|
|
9654
9469
|
result.changes = changes;
|
|
9655
9470
|
return result;
|
|
9656
9471
|
}
|
|
9657
|
-
// Sort changes: deletes first (to free names), then creates, then updates
|
|
9658
9472
|
const sortedChanges = [...changes].sort((a, b) => {
|
|
9659
9473
|
const order = { DELETE: 0, CREATE: 1, UPDATE: 2 };
|
|
9660
9474
|
return order[a.type] - order[b.type];
|
|
@@ -9667,11 +9481,9 @@ function commitChanges(tree, options) {
|
|
|
9667
9481
|
case 'UPDATE':
|
|
9668
9482
|
/* istanbul ignore if -- content is always defined for CREATE/UPDATE from tree.write() */
|
|
9669
9483
|
if (change.content !== undefined) {
|
|
9670
|
-
// Ensure directory exists
|
|
9671
9484
|
const dir = dirname(absPath);
|
|
9672
9485
|
ensureDir(dir);
|
|
9673
9486
|
writeFileSync(absPath, change.content);
|
|
9674
|
-
// Apply permissions if specified
|
|
9675
9487
|
if (change.mode !== undefined) {
|
|
9676
9488
|
chmodSync(absPath, change.mode);
|
|
9677
9489
|
}
|
|
@@ -9689,7 +9501,6 @@ function commitChanges(tree, options) {
|
|
|
9689
9501
|
unlinkSync(absPath);
|
|
9690
9502
|
}
|
|
9691
9503
|
catch {
|
|
9692
|
-
// Try recursive delete for directories
|
|
9693
9504
|
rmSync(absPath, { recursive: true });
|
|
9694
9505
|
}
|
|
9695
9506
|
}
|
|
@@ -9703,7 +9514,6 @@ function commitChanges(tree, options) {
|
|
|
9703
9514
|
}
|
|
9704
9515
|
}
|
|
9705
9516
|
catch (error) {
|
|
9706
|
-
// On error, throw with context
|
|
9707
9517
|
vfsLogger.error('Commit failed', { path: change.path, type: change.type });
|
|
9708
9518
|
const message = error instanceof Error
|
|
9709
9519
|
? `Failed to ${change.type.toLowerCase()} ${change.path}: ${error.message}`
|
|
@@ -9712,7 +9522,6 @@ function commitChanges(tree, options) {
|
|
|
9712
9522
|
}
|
|
9713
9523
|
}
|
|
9714
9524
|
result.changes = appliedChanges;
|
|
9715
|
-
// Clear the tree's pending changes after successful commit
|
|
9716
9525
|
tree.clearChanges();
|
|
9717
9526
|
return result;
|
|
9718
9527
|
}
|
|
@@ -9788,18 +9597,14 @@ function backtrackLcs(table, oldLines, newLines) {
|
|
|
9788
9597
|
* @returns Filtered DiffLine array
|
|
9789
9598
|
*/
|
|
9790
9599
|
function operationsToDiffLines(operations, contextLines) {
|
|
9791
|
-
// Mark which lines should be included (changes + context)
|
|
9792
9600
|
const include = new Array(operations.length).fill(false);
|
|
9793
|
-
// First pass: mark all changes
|
|
9794
9601
|
for (let i = 0; i < operations.length; i++) {
|
|
9795
9602
|
if (operations[i].type !== 'same') {
|
|
9796
|
-
// Mark this line and context around it
|
|
9797
9603
|
for (let j = max(0, i - contextLines); j <= min(operations.length - 1, i + contextLines); j++) {
|
|
9798
9604
|
include[j] = true;
|
|
9799
9605
|
}
|
|
9800
9606
|
}
|
|
9801
9607
|
}
|
|
9802
|
-
// Second pass: convert to DiffLine
|
|
9803
9608
|
const lines = [];
|
|
9804
9609
|
let oldLineNum = 1;
|
|
9805
9610
|
let newLineNum = 1;
|
|
@@ -9821,7 +9626,6 @@ function operationsToDiffLines(operations, contextLines) {
|
|
|
9821
9626
|
}
|
|
9822
9627
|
}
|
|
9823
9628
|
else {
|
|
9824
|
-
// Skip but update line numbers
|
|
9825
9629
|
if (op.type === 'same') {
|
|
9826
9630
|
oldLineNum++;
|
|
9827
9631
|
newLineNum++;
|
|
@@ -9848,7 +9652,6 @@ function bufferToLines(content) {
|
|
|
9848
9652
|
if (!content)
|
|
9849
9653
|
return [];
|
|
9850
9654
|
const text = content.toString('utf-8');
|
|
9851
|
-
// Split by newline, keeping empty last line if present
|
|
9852
9655
|
return text.split('\n');
|
|
9853
9656
|
}
|
|
9854
9657
|
/**
|
|
@@ -9875,9 +9678,7 @@ function generateDiff(change, options = {}) {
|
|
|
9875
9678
|
diffLogger.debug('generateDiff', { path: change.path, type: change.type, contextLines });
|
|
9876
9679
|
const oldLines = bufferToLines(change.originalContent);
|
|
9877
9680
|
const newLines = bufferToLines(change.content);
|
|
9878
|
-
// Handle edge cases
|
|
9879
9681
|
if (change.type === 'CREATE') {
|
|
9880
|
-
// All lines are additions
|
|
9881
9682
|
const lines = newLines
|
|
9882
9683
|
.filter((line) => line !== '' || newLines.indexOf(line) !== newLines.length - 1 || newLines.length === 1)
|
|
9883
9684
|
.map((content, idx) => ({
|
|
@@ -9885,7 +9686,6 @@ function generateDiff(change, options = {}) {
|
|
|
9885
9686
|
line: idx + 1,
|
|
9886
9687
|
content,
|
|
9887
9688
|
}));
|
|
9888
|
-
// Filter out empty trailing line from split
|
|
9889
9689
|
const filteredLines = lines.filter((l, i) => !(i === lines.length - 1 && l.content === '' && lines.length > 1));
|
|
9890
9690
|
return {
|
|
9891
9691
|
path: change.path,
|
|
@@ -9895,13 +9695,11 @@ function generateDiff(change, options = {}) {
|
|
|
9895
9695
|
};
|
|
9896
9696
|
}
|
|
9897
9697
|
if (change.type === 'DELETE') {
|
|
9898
|
-
// All lines are deletions
|
|
9899
9698
|
const lines = oldLines.map((content, idx) => ({
|
|
9900
9699
|
type: 'remove',
|
|
9901
9700
|
line: idx + 1,
|
|
9902
9701
|
content,
|
|
9903
9702
|
}));
|
|
9904
|
-
// Filter out empty trailing line from split
|
|
9905
9703
|
const filteredLines = lines.filter((l, i) => !(i === lines.length - 1 && l.content === '' && lines.length > 1));
|
|
9906
9704
|
return {
|
|
9907
9705
|
path: change.path,
|
|
@@ -9910,7 +9708,6 @@ function generateDiff(change, options = {}) {
|
|
|
9910
9708
|
deletions: filteredLines.length,
|
|
9911
9709
|
};
|
|
9912
9710
|
}
|
|
9913
|
-
// UPDATE: compute actual diff
|
|
9914
9711
|
const table = computeLcsTable(oldLines, newLines);
|
|
9915
9712
|
const operations = backtrackLcs(table, oldLines, newLines);
|
|
9916
9713
|
const lines = operationsToDiffLines(operations, contextLines);
|
|
@@ -9944,13 +9741,11 @@ function generateDiff(change, options = {}) {
|
|
|
9944
9741
|
*/
|
|
9945
9742
|
function formatUnifiedDiff(diff) {
|
|
9946
9743
|
const lines = [];
|
|
9947
|
-
// Header
|
|
9948
9744
|
lines.push(`--- a/${diff.path}`);
|
|
9949
9745
|
lines.push(`+++ b/${diff.path}`);
|
|
9950
9746
|
if (diff.lines.length === 0) {
|
|
9951
9747
|
return lines.join('\n');
|
|
9952
9748
|
}
|
|
9953
|
-
// Group lines into hunks
|
|
9954
9749
|
const hunks = [];
|
|
9955
9750
|
const currentHunk = [];
|
|
9956
9751
|
for (const line of diff.lines) {
|
|
@@ -9959,9 +9754,7 @@ function formatUnifiedDiff(diff) {
|
|
|
9959
9754
|
if (currentHunk.length > 0) {
|
|
9960
9755
|
hunks.push(currentHunk);
|
|
9961
9756
|
}
|
|
9962
|
-
// Output hunks
|
|
9963
9757
|
for (const hunk of hunks) {
|
|
9964
|
-
// Calculate hunk header
|
|
9965
9758
|
const contextAndRemove = hunk.filter((l) => l.type === 'context' || l.type === 'remove');
|
|
9966
9759
|
const contextAndAdd = hunk.filter((l) => l.type === 'context' || l.type === 'add');
|
|
9967
9760
|
const oldStart = hunk.find((l) => l.type === 'context' || l.type === 'remove')?.line ?? 1;
|
|
@@ -9969,7 +9762,6 @@ function formatUnifiedDiff(diff) {
|
|
|
9969
9762
|
const oldCount = contextAndRemove.length;
|
|
9970
9763
|
const newCount = contextAndAdd.length;
|
|
9971
9764
|
lines.push(`@@ -${oldStart},${oldCount} +${newStart},${newCount} @@`);
|
|
9972
|
-
// Output lines
|
|
9973
9765
|
for (const line of hunk) {
|
|
9974
9766
|
const prefix = line.type === 'add' ? '+' : line.type === 'remove' ? '-' : ' ';
|
|
9975
9767
|
lines.push(`${prefix}${line.content}`);
|