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