@hyperfrontend/project-scope 0.2.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 +8 -16
- 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 +6 -62
- package/core/index.cjs.js.map +1 -1
- package/core/index.esm.js +6 -62
- package/core/index.esm.js.map +1 -1
- package/core/logger.d.ts.map +1 -1
- package/core/path/index.cjs.js +0 -1
- package/core/path/index.cjs.js.map +1 -1
- package/core/path/index.esm.js +0 -1
- 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 +81 -293
- package/index.cjs.js.map +1 -1
- package/index.esm.js +81 -293
- 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 +1 -1
- 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 +0 -45
- package/vfs/index.cjs.js.map +1 -1
- package/vfs/index.esm.js +0 -45
- 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
|
}
|
|
@@ -1355,7 +1335,6 @@ function traverseUpward(startPath, predicate) {
|
|
|
1355
1335
|
}
|
|
1356
1336
|
currentPath = node_path.dirname(currentPath);
|
|
1357
1337
|
}
|
|
1358
|
-
// Check root directory
|
|
1359
1338
|
if (predicate(rootPath)) {
|
|
1360
1339
|
fsTraversalLogger.debug('Upward traversal found match at root', { startPath, foundPath: rootPath });
|
|
1361
1340
|
return rootPath;
|
|
@@ -1727,29 +1706,23 @@ const depsLogger = createScopedLogger('project-scope:heuristics:deps');
|
|
|
1727
1706
|
*/
|
|
1728
1707
|
function extractImports(content) {
|
|
1729
1708
|
const imports = [];
|
|
1730
|
-
// ES import with 'from': import X from 'path' or import { X } from 'path'
|
|
1731
|
-
// Use non-greedy match and avoid nested quantifiers by matching "from" keyword directly
|
|
1732
1709
|
const esImportFromRegex = /import\s+.+?\s+from\s+['"]([^'"]+)['"]/g;
|
|
1733
1710
|
let match;
|
|
1734
1711
|
while ((match = esImportFromRegex.exec(content)) !== null) {
|
|
1735
1712
|
imports.push(match[1]);
|
|
1736
1713
|
}
|
|
1737
|
-
// Side-effect import: import 'path'
|
|
1738
1714
|
const sideEffectImportRegex = /import\s+['"]([^'"]+)['"]/g;
|
|
1739
1715
|
while ((match = sideEffectImportRegex.exec(content)) !== null) {
|
|
1740
1716
|
imports.push(match[1]);
|
|
1741
1717
|
}
|
|
1742
|
-
// Dynamic import: import('path')
|
|
1743
1718
|
const dynamicImportRegex = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
1744
1719
|
while ((match = dynamicImportRegex.exec(content)) !== null) {
|
|
1745
1720
|
imports.push(match[1]);
|
|
1746
1721
|
}
|
|
1747
|
-
// require: require('path')
|
|
1748
1722
|
const requireRegex = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
1749
1723
|
while ((match = requireRegex.exec(content)) !== null) {
|
|
1750
1724
|
imports.push(match[1]);
|
|
1751
1725
|
}
|
|
1752
|
-
// Re-export: export * from 'path' or export { X } from 'path'
|
|
1753
1726
|
const exportFromRegex = /export\s+.+?\s+from\s+['"]([^'"]+)['"]/g;
|
|
1754
1727
|
while ((match = exportFromRegex.exec(content)) !== null) {
|
|
1755
1728
|
imports.push(match[1]);
|
|
@@ -2063,7 +2036,6 @@ const cacheRegistry = createSet();
|
|
|
2063
2036
|
function createCache(options) {
|
|
2064
2037
|
const { ttl, maxSize } = options ?? {};
|
|
2065
2038
|
const store = createMap();
|
|
2066
|
-
// Track insertion order for FIFO eviction
|
|
2067
2039
|
const insertionOrder = [];
|
|
2068
2040
|
/**
|
|
2069
2041
|
* Check if an entry is expired.
|
|
@@ -2114,12 +2086,10 @@ function createCache(options) {
|
|
|
2114
2086
|
return entry.value;
|
|
2115
2087
|
},
|
|
2116
2088
|
set(key, value) {
|
|
2117
|
-
// If key exists, remove from order first
|
|
2118
2089
|
if (store.has(key)) {
|
|
2119
2090
|
removeFromOrder(key);
|
|
2120
2091
|
}
|
|
2121
2092
|
else {
|
|
2122
|
-
// Evict if needed before adding new entry
|
|
2123
2093
|
evictIfNeeded();
|
|
2124
2094
|
}
|
|
2125
2095
|
// eslint-disable-next-line workspace/no-unsafe-builtin-methods -- Date.now() is needed for Jest fake timers compatibility
|
|
@@ -2152,7 +2122,6 @@ function createCache(options) {
|
|
|
2152
2122
|
return [...insertionOrder];
|
|
2153
2123
|
},
|
|
2154
2124
|
};
|
|
2155
|
-
// Register cache for global operations
|
|
2156
2125
|
cacheRegistry.add(cache);
|
|
2157
2126
|
return freeze(cache);
|
|
2158
2127
|
}
|
|
@@ -2232,7 +2201,6 @@ function memoize(fn, options) {
|
|
|
2232
2201
|
cache.set(key, result);
|
|
2233
2202
|
return result;
|
|
2234
2203
|
};
|
|
2235
|
-
// Attach cache for direct access
|
|
2236
2204
|
defineProperty(memoized, 'cache', {
|
|
2237
2205
|
value: cache,
|
|
2238
2206
|
writable: false,
|
|
@@ -2241,10 +2209,6 @@ function memoize(fn, options) {
|
|
|
2241
2209
|
return memoized;
|
|
2242
2210
|
}
|
|
2243
2211
|
|
|
2244
|
-
/**
|
|
2245
|
-
* Pattern matching utilities with ReDoS protection.
|
|
2246
|
-
* Uses character-by-character matching instead of regex where possible.
|
|
2247
|
-
*/
|
|
2248
2212
|
/**
|
|
2249
2213
|
* Match path against glob pattern using safe character iteration.
|
|
2250
2214
|
* Avoids regex to prevent ReDoS attacks.
|
|
@@ -2282,17 +2246,14 @@ function matchGlobPattern(path, pattern) {
|
|
|
2282
2246
|
* @returns True if remaining segments match
|
|
2283
2247
|
*/
|
|
2284
2248
|
function matchSegments(pathParts, patternParts, pathIdx, patternIdx) {
|
|
2285
|
-
// Base cases
|
|
2286
2249
|
if (pathIdx === pathParts.length && patternIdx === patternParts.length) {
|
|
2287
|
-
return true;
|
|
2250
|
+
return true;
|
|
2288
2251
|
}
|
|
2289
2252
|
if (patternIdx >= patternParts.length) {
|
|
2290
|
-
return false;
|
|
2253
|
+
return false;
|
|
2291
2254
|
}
|
|
2292
2255
|
const patternPart = patternParts[patternIdx];
|
|
2293
|
-
// Handle ** (globstar) - matches zero or more directories
|
|
2294
2256
|
if (patternPart === '**') {
|
|
2295
|
-
// Try matching rest of pattern against current position and all future positions
|
|
2296
2257
|
for (let i = pathIdx; i <= pathParts.length; i++) {
|
|
2297
2258
|
if (matchSegments(pathParts, patternParts, i, patternIdx + 1)) {
|
|
2298
2259
|
return true;
|
|
@@ -2301,10 +2262,9 @@ function matchSegments(pathParts, patternParts, pathIdx, patternIdx) {
|
|
|
2301
2262
|
return false;
|
|
2302
2263
|
}
|
|
2303
2264
|
if (pathIdx >= pathParts.length) {
|
|
2304
|
-
return false;
|
|
2265
|
+
return false;
|
|
2305
2266
|
}
|
|
2306
2267
|
const pathPart = pathParts[pathIdx];
|
|
2307
|
-
// Match current segment
|
|
2308
2268
|
if (matchSegment(pathPart, patternPart)) {
|
|
2309
2269
|
return matchSegments(pathParts, patternParts, pathIdx + 1, patternIdx + 1);
|
|
2310
2270
|
}
|
|
@@ -2324,12 +2284,10 @@ function matchSegment(text, pattern) {
|
|
|
2324
2284
|
while (patternIdx < pattern.length) {
|
|
2325
2285
|
const char = pattern[patternIdx];
|
|
2326
2286
|
if (char === '*') {
|
|
2327
|
-
// * matches zero or more characters
|
|
2328
2287
|
patternIdx++;
|
|
2329
2288
|
if (patternIdx === pattern.length) {
|
|
2330
|
-
return true;
|
|
2289
|
+
return true;
|
|
2331
2290
|
}
|
|
2332
|
-
// Try matching rest of pattern at each position in text
|
|
2333
2291
|
for (let i = textIdx; i <= text.length; i++) {
|
|
2334
2292
|
if (matchSegmentFrom(text, i, pattern, patternIdx)) {
|
|
2335
2293
|
return true;
|
|
@@ -2338,7 +2296,6 @@ function matchSegment(text, pattern) {
|
|
|
2338
2296
|
return false;
|
|
2339
2297
|
}
|
|
2340
2298
|
else if (char === '?') {
|
|
2341
|
-
// ? matches exactly one character
|
|
2342
2299
|
if (textIdx >= text.length) {
|
|
2343
2300
|
return false;
|
|
2344
2301
|
}
|
|
@@ -2346,10 +2303,8 @@ function matchSegment(text, pattern) {
|
|
|
2346
2303
|
patternIdx++;
|
|
2347
2304
|
}
|
|
2348
2305
|
else if (char === '{') {
|
|
2349
|
-
// {a,b,c} matches any alternative
|
|
2350
2306
|
const closeIdx = findClosingBrace(pattern, patternIdx);
|
|
2351
2307
|
if (closeIdx === -1) {
|
|
2352
|
-
// Unmatched brace, treat as literal
|
|
2353
2308
|
if (textIdx >= text.length || text[textIdx] !== char) {
|
|
2354
2309
|
return false;
|
|
2355
2310
|
}
|
|
@@ -2367,7 +2322,6 @@ function matchSegment(text, pattern) {
|
|
|
2367
2322
|
}
|
|
2368
2323
|
}
|
|
2369
2324
|
else {
|
|
2370
|
-
// Literal character
|
|
2371
2325
|
if (textIdx >= text.length || text[textIdx] !== char) {
|
|
2372
2326
|
return false;
|
|
2373
2327
|
}
|
|
@@ -2793,7 +2747,8 @@ const entryPointLogger = createScopedLogger('project-scope:heuristics:entry-poin
|
|
|
2793
2747
|
*/
|
|
2794
2748
|
const entryPointCache = createCache({ ttl: 60000, maxSize: 50 });
|
|
2795
2749
|
/**
|
|
2796
|
-
* Common entry point patterns.
|
|
2750
|
+
* Common entry point patterns by project type.
|
|
2751
|
+
* Used for convention-based entry point discovery.
|
|
2797
2752
|
*/
|
|
2798
2753
|
const ENTRY_POINT_PATTERNS = {
|
|
2799
2754
|
/** Library entry patterns */
|
|
@@ -3076,7 +3031,6 @@ function collectAllDependencies(packageJson) {
|
|
|
3076
3031
|
function parseVersionString(versionString) {
|
|
3077
3032
|
if (versionString === undefined || versionString === null)
|
|
3078
3033
|
return undefined;
|
|
3079
|
-
// Manual parsing instead of regex to avoid ReDoS
|
|
3080
3034
|
let start = 0;
|
|
3081
3035
|
while (start < versionString.length) {
|
|
3082
3036
|
const char = versionString[start];
|
|
@@ -3137,7 +3091,6 @@ function expressDetector(projectPath, packageJson) {
|
|
|
3137
3091
|
version = parseVersionString(deps['express']);
|
|
3138
3092
|
sources.push({ type: 'package.json', field: 'dependencies.express' });
|
|
3139
3093
|
}
|
|
3140
|
-
// @types/express (indicates usage)
|
|
3141
3094
|
if (deps['@types/express']) {
|
|
3142
3095
|
confidence += 10;
|
|
3143
3096
|
sources.push({ type: 'package.json', field: 'dependencies.@types/express' });
|
|
@@ -3173,13 +3126,11 @@ function nestDetector(projectPath, packageJson) {
|
|
|
3173
3126
|
let version;
|
|
3174
3127
|
let configPath;
|
|
3175
3128
|
const deps = collectAllDependencies(pkg);
|
|
3176
|
-
// @nestjs/core package
|
|
3177
3129
|
if (deps['@nestjs/core']) {
|
|
3178
3130
|
confidence += 70;
|
|
3179
3131
|
version = parseVersionString(deps['@nestjs/core']);
|
|
3180
3132
|
sources.push({ type: 'package.json', field: 'dependencies.@nestjs/core' });
|
|
3181
3133
|
}
|
|
3182
|
-
// @nestjs/common
|
|
3183
3134
|
if (deps['@nestjs/common']) {
|
|
3184
3135
|
confidence += 15;
|
|
3185
3136
|
sources.push({ type: 'package.json', field: 'dependencies.@nestjs/common' });
|
|
@@ -3230,7 +3181,6 @@ function fastifyDetector(projectPath, packageJson) {
|
|
|
3230
3181
|
confidence += 15;
|
|
3231
3182
|
sources.push({ type: 'package.json', field: 'dependencies (fastify plugins)' });
|
|
3232
3183
|
}
|
|
3233
|
-
// @types/fastify (older versions)
|
|
3234
3184
|
if (deps['@types/fastify']) {
|
|
3235
3185
|
confidence += 5;
|
|
3236
3186
|
sources.push({ type: 'package.json', field: 'dependencies.@types/fastify' });
|
|
@@ -3265,7 +3215,6 @@ function koaDetector(projectPath, packageJson) {
|
|
|
3265
3215
|
version = parseVersionString(deps['koa']);
|
|
3266
3216
|
sources.push({ type: 'package.json', field: 'dependencies.koa' });
|
|
3267
3217
|
}
|
|
3268
|
-
// @types/koa
|
|
3269
3218
|
if (deps['@types/koa']) {
|
|
3270
3219
|
confidence += 10;
|
|
3271
3220
|
sources.push({ type: 'package.json', field: 'dependencies.@types/koa' });
|
|
@@ -3844,7 +3793,6 @@ function remixDetector(projectPath, packageJson) {
|
|
|
3844
3793
|
let confidence = 0;
|
|
3845
3794
|
let version;
|
|
3846
3795
|
const deps = collectAllDependencies(pkg);
|
|
3847
|
-
// @remix-run packages
|
|
3848
3796
|
if (deps['@remix-run/react']) {
|
|
3849
3797
|
confidence += 70;
|
|
3850
3798
|
version = parseVersionString(deps['@remix-run/react']);
|
|
@@ -4120,7 +4068,6 @@ function sveltekitDetector(projectPath, packageJson) {
|
|
|
4120
4068
|
let confidence = 0;
|
|
4121
4069
|
let version;
|
|
4122
4070
|
const deps = collectAllDependencies(pkg);
|
|
4123
|
-
// @sveltejs/kit package
|
|
4124
4071
|
if (deps['@sveltejs/kit']) {
|
|
4125
4072
|
confidence += 70;
|
|
4126
4073
|
version = parseVersionString(deps['@sveltejs/kit']);
|
|
@@ -4199,13 +4146,11 @@ function qwikDetector(projectPath, packageJson) {
|
|
|
4199
4146
|
let confidence = 0;
|
|
4200
4147
|
let version;
|
|
4201
4148
|
const deps = collectAllDependencies(pkg);
|
|
4202
|
-
// @builder.io/qwik package
|
|
4203
4149
|
if (deps['@builder.io/qwik']) {
|
|
4204
4150
|
confidence += 70;
|
|
4205
4151
|
version = parseVersionString(deps['@builder.io/qwik']);
|
|
4206
4152
|
sources.push({ type: 'package.json', field: 'dependencies.@builder.io/qwik' });
|
|
4207
4153
|
}
|
|
4208
|
-
// @builder.io/qwik-city
|
|
4209
4154
|
if (deps['@builder.io/qwik-city']) {
|
|
4210
4155
|
confidence += 20;
|
|
4211
4156
|
sources.push({ type: 'package.json', field: 'dependencies.@builder.io/qwik-city' });
|
|
@@ -4316,23 +4261,19 @@ function angularJSDetector(projectPath, packageJson) {
|
|
|
4316
4261
|
let confidence = 0;
|
|
4317
4262
|
let version;
|
|
4318
4263
|
const deps = collectAllDependencies(pkg);
|
|
4319
|
-
// AngularJS package (angular, not @angular/core)
|
|
4320
4264
|
if (deps['angular']) {
|
|
4321
4265
|
confidence += 70;
|
|
4322
4266
|
version = parseVersionString(deps['angular']);
|
|
4323
4267
|
sources.push({ type: 'package.json', field: 'dependencies.angular' });
|
|
4324
4268
|
}
|
|
4325
|
-
// AngularJS router
|
|
4326
4269
|
if (deps['angular-route']) {
|
|
4327
4270
|
confidence += 15;
|
|
4328
4271
|
sources.push({ type: 'package.json', field: 'dependencies.angular-route' });
|
|
4329
4272
|
}
|
|
4330
|
-
// AngularJS resource
|
|
4331
4273
|
if (deps['angular-resource']) {
|
|
4332
4274
|
confidence += 10;
|
|
4333
4275
|
sources.push({ type: 'package.json', field: 'dependencies.angular-resource' });
|
|
4334
4276
|
}
|
|
4335
|
-
// AngularJS animate
|
|
4336
4277
|
if (deps['angular-animate']) {
|
|
4337
4278
|
confidence += 5;
|
|
4338
4279
|
sources.push({ type: 'package.json', field: 'dependencies.angular-animate' });
|
|
@@ -4363,23 +4304,19 @@ function backboneDetector(projectPath, packageJson) {
|
|
|
4363
4304
|
let confidence = 0;
|
|
4364
4305
|
let version;
|
|
4365
4306
|
const deps = collectAllDependencies(pkg);
|
|
4366
|
-
// Backbone package
|
|
4367
4307
|
if (deps['backbone']) {
|
|
4368
4308
|
confidence += 70;
|
|
4369
4309
|
version = parseVersionString(deps['backbone']);
|
|
4370
4310
|
sources.push({ type: 'package.json', field: 'dependencies.backbone' });
|
|
4371
|
-
// Underscore (commonly used with Backbone)
|
|
4372
4311
|
if (deps['underscore']) {
|
|
4373
4312
|
confidence += 15;
|
|
4374
4313
|
sources.push({ type: 'package.json', field: 'dependencies.underscore' });
|
|
4375
4314
|
}
|
|
4376
|
-
// Lodash can be used as underscore replacement
|
|
4377
4315
|
if (deps['lodash']) {
|
|
4378
4316
|
confidence += 5;
|
|
4379
4317
|
sources.push({ type: 'package.json', field: 'dependencies.lodash' });
|
|
4380
4318
|
}
|
|
4381
4319
|
}
|
|
4382
|
-
// Marionette (Backbone framework)
|
|
4383
4320
|
if (deps['backbone.marionette'] || deps['marionette']) {
|
|
4384
4321
|
confidence += 10;
|
|
4385
4322
|
sources.push({ type: 'package.json', field: 'dependencies.backbone.marionette' });
|
|
@@ -4410,18 +4347,15 @@ function emberDetector(projectPath, packageJson) {
|
|
|
4410
4347
|
let confidence = 0;
|
|
4411
4348
|
let version;
|
|
4412
4349
|
const deps = collectAllDependencies(pkg);
|
|
4413
|
-
// Ember source package
|
|
4414
4350
|
if (deps['ember-source']) {
|
|
4415
4351
|
confidence += 70;
|
|
4416
4352
|
version = parseVersionString(deps['ember-source']);
|
|
4417
4353
|
sources.push({ type: 'package.json', field: 'dependencies.ember-source' });
|
|
4418
4354
|
}
|
|
4419
|
-
// Ember CLI
|
|
4420
4355
|
if (deps['ember-cli']) {
|
|
4421
4356
|
confidence += 20;
|
|
4422
4357
|
sources.push({ type: 'package.json', field: 'devDependencies.ember-cli' });
|
|
4423
4358
|
}
|
|
4424
|
-
// Ember Data
|
|
4425
4359
|
if (deps['ember-data']) {
|
|
4426
4360
|
confidence += 10;
|
|
4427
4361
|
sources.push({ type: 'package.json', field: 'dependencies.ember-data' });
|
|
@@ -4452,18 +4386,15 @@ function jqueryDetector(projectPath, packageJson) {
|
|
|
4452
4386
|
let confidence = 0;
|
|
4453
4387
|
let version;
|
|
4454
4388
|
const deps = collectAllDependencies(pkg);
|
|
4455
|
-
// jQuery package
|
|
4456
4389
|
if (deps['jquery']) {
|
|
4457
4390
|
confidence += 80;
|
|
4458
4391
|
version = parseVersionString(deps['jquery']);
|
|
4459
4392
|
sources.push({ type: 'package.json', field: 'dependencies.jquery' });
|
|
4460
4393
|
}
|
|
4461
|
-
// jQuery UI
|
|
4462
4394
|
if (deps['jquery-ui']) {
|
|
4463
4395
|
confidence += 10;
|
|
4464
4396
|
sources.push({ type: 'package.json', field: 'dependencies.jquery-ui' });
|
|
4465
4397
|
}
|
|
4466
|
-
// jQuery plugins
|
|
4467
4398
|
if (deps['jquery-validation']) {
|
|
4468
4399
|
confidence += 5;
|
|
4469
4400
|
sources.push({ type: 'package.json', field: 'dependencies.jquery-validation' });
|
|
@@ -4610,7 +4541,6 @@ function prettierDetector(projectPath, packageJson) {
|
|
|
4610
4541
|
confidence += 30;
|
|
4611
4542
|
sources.push({ type: 'package.json', field: 'prettier' });
|
|
4612
4543
|
}
|
|
4613
|
-
// .prettierignore file
|
|
4614
4544
|
if (exists(node_path.join(projectPath, '.prettierignore'))) {
|
|
4615
4545
|
confidence += 10;
|
|
4616
4546
|
sources.push({ type: 'config-file', path: '.prettierignore' });
|
|
@@ -4705,7 +4635,6 @@ function biomeDetector(projectPath, packageJson) {
|
|
|
4705
4635
|
let configPath;
|
|
4706
4636
|
let version;
|
|
4707
4637
|
const deps = collectAllDependencies(pkg);
|
|
4708
|
-
// @biomejs/biome package
|
|
4709
4638
|
if (deps['@biomejs/biome']) {
|
|
4710
4639
|
confidence += 70;
|
|
4711
4640
|
version = parseVersionString(deps['@biomejs/biome']);
|
|
@@ -4992,7 +4921,7 @@ function npmWorkspacesDetector(workspacePath, packageJson) {
|
|
|
4992
4921
|
sources.push({ type: 'lockfile', path: 'package-lock.json' });
|
|
4993
4922
|
}
|
|
4994
4923
|
if (exists(node_path.join(workspacePath, 'yarn.lock'))) {
|
|
4995
|
-
return null;
|
|
4924
|
+
return null;
|
|
4996
4925
|
}
|
|
4997
4926
|
if (confidence === 0) {
|
|
4998
4927
|
return null;
|
|
@@ -5371,7 +5300,6 @@ function checkTsConfigStrict(projectPath) {
|
|
|
5371
5300
|
if (!content)
|
|
5372
5301
|
return undefined;
|
|
5373
5302
|
try {
|
|
5374
|
-
// Simple JSON parsing - doesn't handle comments but good enough for strict check
|
|
5375
5303
|
const cleanContent = content.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, '');
|
|
5376
5304
|
const parsed = parse(cleanContent);
|
|
5377
5305
|
return parsed?.compilerOptions?.strict === true;
|
|
@@ -5394,19 +5322,16 @@ function typescriptDetector(projectPath, packageJson) {
|
|
|
5394
5322
|
let configPath;
|
|
5395
5323
|
let version;
|
|
5396
5324
|
const deps = collectAllDependencies(pkg);
|
|
5397
|
-
// TypeScript package
|
|
5398
5325
|
if (deps['typescript']) {
|
|
5399
5326
|
confidence += 50;
|
|
5400
5327
|
version = parseVersionString(deps['typescript']);
|
|
5401
5328
|
sources.push({ type: 'package.json', field: 'dependencies.typescript' });
|
|
5402
5329
|
}
|
|
5403
|
-
// tsconfig.json
|
|
5404
5330
|
if (exists(node_path.join(projectPath, 'tsconfig.json'))) {
|
|
5405
5331
|
confidence += 40;
|
|
5406
5332
|
configPath = 'tsconfig.json';
|
|
5407
5333
|
sources.push({ type: 'config-file', path: 'tsconfig.json' });
|
|
5408
5334
|
}
|
|
5409
|
-
// tsconfig.*.json variants
|
|
5410
5335
|
const tsconfigVariants = ['tsconfig.build.json', 'tsconfig.lib.json', 'tsconfig.spec.json', 'tsconfig.app.json'];
|
|
5411
5336
|
for (const variant of tsconfigVariants) {
|
|
5412
5337
|
if (exists(node_path.join(projectPath, variant))) {
|
|
@@ -5415,7 +5340,6 @@ function typescriptDetector(projectPath, packageJson) {
|
|
|
5415
5340
|
break;
|
|
5416
5341
|
}
|
|
5417
5342
|
}
|
|
5418
|
-
// @types packages
|
|
5419
5343
|
const typePackages = keys(deps).filter((d) => d.startsWith('@types/'));
|
|
5420
5344
|
if (typePackages.length > 0) {
|
|
5421
5345
|
confidence += 10;
|
|
@@ -5449,24 +5373,20 @@ function flowDetector(projectPath, packageJson) {
|
|
|
5449
5373
|
let configPath;
|
|
5450
5374
|
let version;
|
|
5451
5375
|
const deps = collectAllDependencies(pkg);
|
|
5452
|
-
// flow-bin package
|
|
5453
5376
|
if (deps['flow-bin']) {
|
|
5454
5377
|
confidence += 60;
|
|
5455
5378
|
version = parseVersionString(deps['flow-bin']);
|
|
5456
5379
|
sources.push({ type: 'package.json', field: 'dependencies.flow-bin' });
|
|
5457
5380
|
}
|
|
5458
|
-
// .flowconfig
|
|
5459
5381
|
if (exists(node_path.join(projectPath, '.flowconfig'))) {
|
|
5460
5382
|
confidence += 40;
|
|
5461
5383
|
configPath = '.flowconfig';
|
|
5462
5384
|
sources.push({ type: 'config-file', path: '.flowconfig' });
|
|
5463
5385
|
}
|
|
5464
|
-
// flow-typed directory
|
|
5465
5386
|
if (exists(node_path.join(projectPath, 'flow-typed'))) {
|
|
5466
5387
|
confidence += 10;
|
|
5467
5388
|
sources.push({ type: 'directory', path: 'flow-typed/' });
|
|
5468
5389
|
}
|
|
5469
|
-
// @babel/preset-flow
|
|
5470
5390
|
if (deps['@babel/preset-flow']) {
|
|
5471
5391
|
confidence += 10;
|
|
5472
5392
|
sources.push({ type: 'package.json', field: 'dependencies.@babel/preset-flow' });
|
|
@@ -5490,7 +5410,6 @@ function flowDetector(projectPath, packageJson) {
|
|
|
5490
5410
|
* @returns `true` if the content contains JSDoc type annotations.
|
|
5491
5411
|
*/
|
|
5492
5412
|
function hasJsDocTypes(content) {
|
|
5493
|
-
// Check for JSDoc type annotations
|
|
5494
5413
|
return (content.includes('@type {') ||
|
|
5495
5414
|
content.includes('@param {') ||
|
|
5496
5415
|
content.includes('@returns {') ||
|
|
@@ -5509,14 +5428,11 @@ function jsdocDetector(projectPath, packageJson) {
|
|
|
5509
5428
|
const sources = [];
|
|
5510
5429
|
let confidence = 0;
|
|
5511
5430
|
const deps = collectAllDependencies(pkg);
|
|
5512
|
-
// jsdoc package
|
|
5513
5431
|
if (deps['jsdoc']) {
|
|
5514
5432
|
confidence += 30;
|
|
5515
5433
|
sources.push({ type: 'package.json', field: 'dependencies.jsdoc' });
|
|
5516
5434
|
}
|
|
5517
|
-
// typescript with checkJs (JSDoc type checking)
|
|
5518
5435
|
if (deps['typescript']) {
|
|
5519
|
-
// Check if checkJs is enabled in tsconfig
|
|
5520
5436
|
const tsconfigPath = node_path.join(projectPath, 'tsconfig.json');
|
|
5521
5437
|
const content = readFileIfExists(tsconfigPath);
|
|
5522
5438
|
if (content) {
|
|
@@ -5533,12 +5449,10 @@ function jsdocDetector(projectPath, packageJson) {
|
|
|
5533
5449
|
}
|
|
5534
5450
|
}
|
|
5535
5451
|
}
|
|
5536
|
-
// Check for jsconfig.json (VS Code JS type checking)
|
|
5537
5452
|
if (exists(node_path.join(projectPath, 'jsconfig.json'))) {
|
|
5538
5453
|
confidence += 40;
|
|
5539
5454
|
sources.push({ type: 'config-file', path: 'jsconfig.json' });
|
|
5540
5455
|
}
|
|
5541
|
-
// Sample check for JSDoc annotations in source files
|
|
5542
5456
|
const srcDir = node_path.join(projectPath, 'src');
|
|
5543
5457
|
if (exists(srcDir)) {
|
|
5544
5458
|
try {
|
|
@@ -5620,8 +5534,6 @@ const allDetectors = {
|
|
|
5620
5534
|
function isDetectAllOptions(value) {
|
|
5621
5535
|
if (typeof value !== 'object' || value === null)
|
|
5622
5536
|
return false;
|
|
5623
|
-
// DetectAllOptions has skipCache or packageJson fields specifically
|
|
5624
|
-
// PackageJson never has skipCache field
|
|
5625
5537
|
return 'skipCache' in value || 'packageJson' in value;
|
|
5626
5538
|
}
|
|
5627
5539
|
/**
|
|
@@ -5653,9 +5565,7 @@ function isDetectAllOptions(value) {
|
|
|
5653
5565
|
* ```
|
|
5654
5566
|
*/
|
|
5655
5567
|
function detectAll(projectPath, packageJsonOrOptions) {
|
|
5656
|
-
// Handle backward-compatible arguments
|
|
5657
5568
|
const options = isDetectAllOptions(packageJsonOrOptions) ? packageJsonOrOptions : { packageJson: packageJsonOrOptions };
|
|
5658
|
-
// Check cache first (unless skipCache is true)
|
|
5659
5569
|
if (!options.skipCache) {
|
|
5660
5570
|
const cached = detectAllCache.get(projectPath);
|
|
5661
5571
|
if (cached) {
|
|
@@ -5706,7 +5616,6 @@ function detectAll(projectPath, packageJsonOrOptions) {
|
|
|
5706
5616
|
legacyFrameworks: result.legacyFrameworks.map((f) => f.id),
|
|
5707
5617
|
testingFrameworks: result.testingFrameworks.map((f) => f.id),
|
|
5708
5618
|
});
|
|
5709
|
-
// Cache the result
|
|
5710
5619
|
detectAllCache.set(projectPath, result);
|
|
5711
5620
|
return result;
|
|
5712
5621
|
}
|
|
@@ -6123,7 +6032,6 @@ function detectNxVersion(workspacePath) {
|
|
|
6123
6032
|
if (packageJson) {
|
|
6124
6033
|
const nxVersion = packageJson.devDependencies?.['nx'] ?? packageJson.dependencies?.['nx'];
|
|
6125
6034
|
if (nxVersion) {
|
|
6126
|
-
// Strip semver range characters (^, ~, >=, etc.)
|
|
6127
6035
|
return nxVersion.replace(/^[\^~>=<]+/, '');
|
|
6128
6036
|
}
|
|
6129
6037
|
}
|
|
@@ -6152,14 +6060,12 @@ function getNxWorkspaceInfo(workspacePath) {
|
|
|
6152
6060
|
}
|
|
6153
6061
|
const nxJson = readJsonFileIfExists(node_path.join(workspacePath, 'nx.json'));
|
|
6154
6062
|
if (!nxJson) {
|
|
6155
|
-
// Check for workspace.json as fallback (older NX)
|
|
6156
6063
|
const workspaceJson = readJsonFileIfExists(node_path.join(workspacePath, 'workspace.json'));
|
|
6157
6064
|
if (!workspaceJson) {
|
|
6158
6065
|
nxLogger.debug('No nx.json or workspace.json found', { workspacePath });
|
|
6159
6066
|
return null;
|
|
6160
6067
|
}
|
|
6161
6068
|
nxLogger.debug('Using legacy workspace.json', { workspacePath });
|
|
6162
|
-
// Create minimal nx.json from workspace.json
|
|
6163
6069
|
return {
|
|
6164
6070
|
root: workspacePath,
|
|
6165
6071
|
version: detectNxVersion(workspacePath),
|
|
@@ -6208,7 +6114,6 @@ function tryLoadDevkit() {
|
|
|
6208
6114
|
}
|
|
6209
6115
|
devkitLogger.debug('Attempting to load @nx/devkit');
|
|
6210
6116
|
try {
|
|
6211
|
-
// Dynamic require to avoid bundling
|
|
6212
6117
|
const devkit = require('@nx/devkit');
|
|
6213
6118
|
devkitLogger.debug('@nx/devkit loaded successfully');
|
|
6214
6119
|
cachedResult = { available: true, devkit };
|
|
@@ -6305,7 +6210,6 @@ function readProjectJson(projectPath) {
|
|
|
6305
6210
|
*/
|
|
6306
6211
|
function getProjectConfig(projectPath, workspacePath) {
|
|
6307
6212
|
nxConfigLogger.debug('Getting project config', { projectPath, workspacePath });
|
|
6308
|
-
// Try project.json first
|
|
6309
6213
|
const projectJson = readProjectJson(projectPath);
|
|
6310
6214
|
if (projectJson) {
|
|
6311
6215
|
nxConfigLogger.debug('Using project.json config', { projectPath, name: projectJson.name });
|
|
@@ -6314,7 +6218,6 @@ function getProjectConfig(projectPath, workspacePath) {
|
|
|
6314
6218
|
root: projectJson.root ?? node_path.relative(workspacePath, projectPath),
|
|
6315
6219
|
};
|
|
6316
6220
|
}
|
|
6317
|
-
// Try to infer from package.json nx field
|
|
6318
6221
|
const packageJson = readPackageJsonIfExists(projectPath);
|
|
6319
6222
|
if (packageJson && typeof packageJson['nx'] === 'object') {
|
|
6320
6223
|
nxConfigLogger.debug('Using package.json nx field', { projectPath, name: packageJson.name });
|
|
@@ -6343,13 +6246,11 @@ function scanForProjects(dirPath, workspacePath, projects, maxDepth, currentDept
|
|
|
6343
6246
|
try {
|
|
6344
6247
|
const entries = readDirectory(dirPath);
|
|
6345
6248
|
for (const entry of entries) {
|
|
6346
|
-
// Skip node_modules and hidden directories
|
|
6347
6249
|
if (entry.name.startsWith('.') || entry.name === 'node_modules' || entry.name === 'dist') {
|
|
6348
6250
|
continue;
|
|
6349
6251
|
}
|
|
6350
6252
|
const fullPath = node_path.join(dirPath, entry.name);
|
|
6351
6253
|
if (entry.isDirectory) {
|
|
6352
|
-
// Check if this directory is an NX project
|
|
6353
6254
|
if (isNxProject(fullPath)) {
|
|
6354
6255
|
const config = getProjectConfig(fullPath, workspacePath);
|
|
6355
6256
|
if (config) {
|
|
@@ -6361,7 +6262,6 @@ function scanForProjects(dirPath, workspacePath, projects, maxDepth, currentDept
|
|
|
6361
6262
|
});
|
|
6362
6263
|
}
|
|
6363
6264
|
}
|
|
6364
|
-
// Recursively scan subdirectories
|
|
6365
6265
|
scanForProjects(fullPath, workspacePath, projects, maxDepth, currentDepth + 1);
|
|
6366
6266
|
}
|
|
6367
6267
|
}
|
|
@@ -6379,12 +6279,10 @@ function scanForProjects(dirPath, workspacePath, projects, maxDepth, currentDept
|
|
|
6379
6279
|
*/
|
|
6380
6280
|
function discoverNxProjects(workspacePath) {
|
|
6381
6281
|
const projects = createMap();
|
|
6382
|
-
// Check for workspace.json (older NX format)
|
|
6383
6282
|
const workspaceJson = readJsonFileIfExists(node_path.join(workspacePath, 'workspace.json'));
|
|
6384
6283
|
if (workspaceJson?.projects) {
|
|
6385
6284
|
for (const [name, config] of entries(workspaceJson.projects)) {
|
|
6386
6285
|
if (typeof config === 'string') {
|
|
6387
|
-
// Path reference to project directory
|
|
6388
6286
|
const projectPath = node_path.join(workspacePath, config);
|
|
6389
6287
|
const projectConfig = getProjectConfig(projectPath, workspacePath);
|
|
6390
6288
|
if (projectConfig) {
|
|
@@ -6392,18 +6290,15 @@ function discoverNxProjects(workspacePath) {
|
|
|
6392
6290
|
}
|
|
6393
6291
|
}
|
|
6394
6292
|
else if (typeof config === 'object' && config !== null) {
|
|
6395
|
-
// Inline config
|
|
6396
6293
|
projects.set(name, { name, ...config });
|
|
6397
6294
|
}
|
|
6398
6295
|
}
|
|
6399
6296
|
return projects;
|
|
6400
6297
|
}
|
|
6401
|
-
// Scan for project.json files (newer NX format)
|
|
6402
6298
|
const workspaceInfo = getNxWorkspaceInfo(workspacePath);
|
|
6403
6299
|
const appsDir = workspaceInfo?.workspaceLayout.appsDir ?? 'apps';
|
|
6404
6300
|
const libsDir = workspaceInfo?.workspaceLayout.libsDir ?? 'libs';
|
|
6405
6301
|
const searchDirs = [appsDir, libsDir];
|
|
6406
|
-
// Also check packages directory (common in some setups)
|
|
6407
6302
|
if (exists(node_path.join(workspacePath, 'packages'))) {
|
|
6408
6303
|
searchDirs.push('packages');
|
|
6409
6304
|
}
|
|
@@ -6418,7 +6313,6 @@ function discoverNxProjects(workspacePath) {
|
|
|
6418
6313
|
}
|
|
6419
6314
|
}
|
|
6420
6315
|
}
|
|
6421
|
-
// Also check root-level projects (standalone projects in monorepo root)
|
|
6422
6316
|
if (isNxProject(workspacePath)) {
|
|
6423
6317
|
const config = readProjectJson(workspacePath);
|
|
6424
6318
|
if (config) {
|
|
@@ -6451,10 +6345,8 @@ function buildSimpleProjectGraph(workspacePath, projects) {
|
|
|
6451
6345
|
data: config,
|
|
6452
6346
|
};
|
|
6453
6347
|
dependencies[name] = [];
|
|
6454
|
-
// Add implicit dependencies
|
|
6455
6348
|
if (config.implicitDependencies) {
|
|
6456
6349
|
for (const dep of config.implicitDependencies) {
|
|
6457
|
-
// Skip negative dependencies (those starting with !)
|
|
6458
6350
|
if (!dep.startsWith('!')) {
|
|
6459
6351
|
dependencies[name].push({
|
|
6460
6352
|
target: dep,
|
|
@@ -6471,7 +6363,6 @@ function buildSimpleProjectGraph(workspacePath, projects) {
|
|
|
6471
6363
|
* Known configuration file patterns organized by type.
|
|
6472
6364
|
*/
|
|
6473
6365
|
const CONFIG_PATTERNS = {
|
|
6474
|
-
// Package Management
|
|
6475
6366
|
'package.json': {
|
|
6476
6367
|
patterns: ['package.json'],
|
|
6477
6368
|
format: 'json',
|
|
@@ -6498,14 +6389,12 @@ const CONFIG_PATTERNS = {
|
|
|
6498
6389
|
description: 'NPM configuration',
|
|
6499
6390
|
sensitive: true,
|
|
6500
6391
|
},
|
|
6501
|
-
// TypeScript
|
|
6502
6392
|
tsconfig: {
|
|
6503
6393
|
patterns: ['tsconfig.json', 'tsconfig.*.json'],
|
|
6504
6394
|
format: 'jsonc',
|
|
6505
6395
|
description: 'TypeScript configuration',
|
|
6506
6396
|
canExtend: true,
|
|
6507
6397
|
},
|
|
6508
|
-
// Monorepo
|
|
6509
6398
|
nx: {
|
|
6510
6399
|
patterns: ['nx.json'],
|
|
6511
6400
|
format: 'json',
|
|
@@ -6531,7 +6420,6 @@ const CONFIG_PATTERNS = {
|
|
|
6531
6420
|
format: 'json',
|
|
6532
6421
|
description: 'Lerna configuration',
|
|
6533
6422
|
},
|
|
6534
|
-
// Build Tools
|
|
6535
6423
|
webpack: {
|
|
6536
6424
|
patterns: ['webpack.config.js', 'webpack.config.ts', 'webpack.config.cjs', 'webpack.config.mjs'],
|
|
6537
6425
|
format: 'js',
|
|
@@ -6562,7 +6450,6 @@ const CONFIG_PATTERNS = {
|
|
|
6562
6450
|
format: 'json',
|
|
6563
6451
|
description: 'SWC configuration',
|
|
6564
6452
|
},
|
|
6565
|
-
// Testing
|
|
6566
6453
|
jest: {
|
|
6567
6454
|
patterns: ['jest.config.js', 'jest.config.ts', 'jest.config.mjs'],
|
|
6568
6455
|
description: 'Jest configuration',
|
|
@@ -6579,7 +6466,6 @@ const CONFIG_PATTERNS = {
|
|
|
6579
6466
|
patterns: ['playwright.config.js', 'playwright.config.ts'],
|
|
6580
6467
|
description: 'Playwright configuration',
|
|
6581
6468
|
},
|
|
6582
|
-
// Framework configs
|
|
6583
6469
|
next: {
|
|
6584
6470
|
patterns: ['next.config.js', 'next.config.mjs', 'next.config.ts'],
|
|
6585
6471
|
format: 'js',
|
|
@@ -6605,7 +6491,6 @@ const CONFIG_PATTERNS = {
|
|
|
6605
6491
|
format: 'js',
|
|
6606
6492
|
description: 'Astro configuration',
|
|
6607
6493
|
},
|
|
6608
|
-
// Linting & Formatting
|
|
6609
6494
|
eslint: {
|
|
6610
6495
|
patterns: [
|
|
6611
6496
|
'eslint.config.js',
|
|
@@ -6624,14 +6509,12 @@ const CONFIG_PATTERNS = {
|
|
|
6624
6509
|
format: 'json',
|
|
6625
6510
|
description: 'Prettier configuration',
|
|
6626
6511
|
},
|
|
6627
|
-
// Environment (sensitive)
|
|
6628
6512
|
env: {
|
|
6629
6513
|
patterns: ['.env', '.env.*', '*.env'],
|
|
6630
6514
|
format: 'dotenv',
|
|
6631
6515
|
description: 'Environment variables',
|
|
6632
6516
|
sensitive: true,
|
|
6633
6517
|
},
|
|
6634
|
-
// Git
|
|
6635
6518
|
'.gitignore': {
|
|
6636
6519
|
patterns: ['.gitignore'],
|
|
6637
6520
|
format: 'text',
|
|
@@ -6802,12 +6685,8 @@ function getConfigPaths(type) {
|
|
|
6802
6685
|
*
|
|
6803
6686
|
* @module @hyperfrontend/immutable-api-utils/built-in-copy/number
|
|
6804
6687
|
*/
|
|
6805
|
-
// Capture references at module initialization time
|
|
6806
6688
|
const _parseInt = globalThis.parseInt;
|
|
6807
6689
|
const _parseFloat = globalThis.parseFloat;
|
|
6808
|
-
// ============================================================================
|
|
6809
|
-
// Parsing
|
|
6810
|
-
// ============================================================================
|
|
6811
6690
|
/**
|
|
6812
6691
|
* (Safe copy) Parses a string and returns an integer.
|
|
6813
6692
|
*/
|
|
@@ -7162,11 +7041,9 @@ const analyzeLogger = createScopedLogger('project-scope:analyze');
|
|
|
7162
7041
|
* @returns Detected workspace type
|
|
7163
7042
|
*/
|
|
7164
7043
|
function detectWorkspaceType(projectPath) {
|
|
7165
|
-
// Check for NX
|
|
7166
7044
|
if (isNxWorkspace(projectPath) || findNxWorkspaceRoot(projectPath) !== null) {
|
|
7167
7045
|
return 'nx';
|
|
7168
7046
|
}
|
|
7169
|
-
// Check for common monorepo markers
|
|
7170
7047
|
if (exists(node_path.resolve(projectPath, 'turbo.json'))) {
|
|
7171
7048
|
return 'turborepo';
|
|
7172
7049
|
}
|
|
@@ -7176,7 +7053,6 @@ function detectWorkspaceType(projectPath) {
|
|
|
7176
7053
|
if (exists(node_path.resolve(projectPath, 'pnpm-workspace.yaml'))) {
|
|
7177
7054
|
return 'pnpm';
|
|
7178
7055
|
}
|
|
7179
|
-
// Check package.json for workspaces
|
|
7180
7056
|
const pkg = readPackageJsonIfExists(projectPath);
|
|
7181
7057
|
if (pkg?.workspaces) {
|
|
7182
7058
|
if (exists(node_path.resolve(projectPath, 'yarn.lock'))) {
|
|
@@ -7185,7 +7061,7 @@ function detectWorkspaceType(projectPath) {
|
|
|
7185
7061
|
if (exists(node_path.resolve(projectPath, 'package-lock.json'))) {
|
|
7186
7062
|
return 'npm';
|
|
7187
7063
|
}
|
|
7188
|
-
return 'npm';
|
|
7064
|
+
return 'npm';
|
|
7189
7065
|
}
|
|
7190
7066
|
return 'standalone';
|
|
7191
7067
|
}
|
|
@@ -7197,15 +7073,12 @@ function detectWorkspaceType(projectPath) {
|
|
|
7197
7073
|
* @returns True if the analysis should be performed
|
|
7198
7074
|
*/
|
|
7199
7075
|
function shouldInclude(type, options) {
|
|
7200
|
-
// If include list is specified, item must be in it
|
|
7201
7076
|
if (options?.include && options.include.length > 0) {
|
|
7202
7077
|
return options.include.includes(type);
|
|
7203
7078
|
}
|
|
7204
|
-
// If exclude list is specified, item must not be in it
|
|
7205
7079
|
if (options?.exclude && options.exclude.length > 0) {
|
|
7206
7080
|
return !options.exclude.includes(type);
|
|
7207
7081
|
}
|
|
7208
|
-
// Default: include everything
|
|
7209
7082
|
return true;
|
|
7210
7083
|
}
|
|
7211
7084
|
/**
|
|
@@ -7254,28 +7127,22 @@ function normalizeConfigFormat(format) {
|
|
|
7254
7127
|
function analyzeProject(projectPath, options) {
|
|
7255
7128
|
const startTime = dateNow();
|
|
7256
7129
|
const resolvedPath = node_path.resolve(projectPath);
|
|
7257
|
-
// Set log level based on verbose option
|
|
7258
7130
|
if (options?.verbose) {
|
|
7259
7131
|
analyzeLogger.setLogLevel('debug');
|
|
7260
7132
|
}
|
|
7261
7133
|
analyzeLogger.debug('Starting project analysis', { path: resolvedPath, options });
|
|
7262
|
-
// Read package.json once for reuse
|
|
7263
7134
|
const packageJson = readPackageJsonIfExists(resolvedPath);
|
|
7264
7135
|
analyzeLogger.debug('Package.json loaded', { found: !!packageJson, name: packageJson?.name });
|
|
7265
|
-
// Detect project and workspace types
|
|
7266
7136
|
const projectTypeDetection = detectProjectType(resolvedPath, {
|
|
7267
7137
|
skipTechDetection: options?.depth === 'basic',
|
|
7268
7138
|
});
|
|
7269
7139
|
const projectType = projectTypeDetection.type;
|
|
7270
7140
|
const workspaceType = detectWorkspaceType(resolvedPath);
|
|
7271
7141
|
analyzeLogger.debug('Project type detected', { projectType, workspaceType });
|
|
7272
|
-
// Get project name from package.json or directory name
|
|
7273
7142
|
const projectName = packageJson?.name ?? node_path.basename(resolvedPath);
|
|
7274
|
-
// Run all technology detectors
|
|
7275
7143
|
const detections = shouldInclude('frameworks', options) || shouldInclude('buildTools', options) || shouldInclude('testing', options)
|
|
7276
7144
|
? detectAll(resolvedPath, packageJson ?? undefined)
|
|
7277
7145
|
: null;
|
|
7278
|
-
// Convert framework detections to FrameworkInfo
|
|
7279
7146
|
const frameworks = shouldInclude('frameworks', options) && detections
|
|
7280
7147
|
? [
|
|
7281
7148
|
...detections.frontendFrameworks.map((d) => ({
|
|
@@ -7295,7 +7162,6 @@ function analyzeProject(projectPath, options) {
|
|
|
7295
7162
|
})),
|
|
7296
7163
|
]
|
|
7297
7164
|
: [];
|
|
7298
|
-
// Convert build tool detections
|
|
7299
7165
|
const buildTools = shouldInclude('buildTools', options) && detections
|
|
7300
7166
|
? detections.buildTools.map((d) => ({
|
|
7301
7167
|
id: d.id,
|
|
@@ -7305,7 +7171,6 @@ function analyzeProject(projectPath, options) {
|
|
|
7305
7171
|
confidence: d.confidence,
|
|
7306
7172
|
}))
|
|
7307
7173
|
: [];
|
|
7308
|
-
// Convert testing framework detections
|
|
7309
7174
|
const testingFrameworks = shouldInclude('testing', options) && detections
|
|
7310
7175
|
? detections.testingFrameworks.map((d) => ({
|
|
7311
7176
|
id: d.id,
|
|
@@ -7316,7 +7181,6 @@ function analyzeProject(projectPath, options) {
|
|
|
7316
7181
|
confidence: d.confidence,
|
|
7317
7182
|
}))
|
|
7318
7183
|
: [];
|
|
7319
|
-
// Discover entry points
|
|
7320
7184
|
const entryPoints = shouldInclude('entryPoints', options)
|
|
7321
7185
|
? discoverEntryPoints(resolvedPath).map((e) => ({
|
|
7322
7186
|
path: e.path,
|
|
@@ -7324,7 +7188,6 @@ function analyzeProject(projectPath, options) {
|
|
|
7324
7188
|
confidence: e.confidence,
|
|
7325
7189
|
}))
|
|
7326
7190
|
: [];
|
|
7327
|
-
// Detect configuration files
|
|
7328
7191
|
const configFiles = shouldInclude('configs', options)
|
|
7329
7192
|
? detectConfigs(resolvedPath).map((c) => ({
|
|
7330
7193
|
path: c.path,
|
|
@@ -7333,7 +7196,6 @@ function analyzeProject(projectPath, options) {
|
|
|
7333
7196
|
tool: c.type,
|
|
7334
7197
|
}))
|
|
7335
7198
|
: [];
|
|
7336
|
-
// Get dependency summary
|
|
7337
7199
|
let dependencies;
|
|
7338
7200
|
if (shouldInclude('dependencies', options)) {
|
|
7339
7201
|
const deps = getProjectDependencies(resolvedPath);
|
|
@@ -7354,7 +7216,6 @@ function analyzeProject(projectPath, options) {
|
|
|
7354
7216
|
total: 0,
|
|
7355
7217
|
};
|
|
7356
7218
|
}
|
|
7357
|
-
// Build metadata
|
|
7358
7219
|
const metadata = {
|
|
7359
7220
|
timestamp: createDate(),
|
|
7360
7221
|
durationMs: dateNow() - startTime,
|
|
@@ -7429,15 +7290,12 @@ function formatWorkspaceType(type) {
|
|
|
7429
7290
|
*/
|
|
7430
7291
|
function formatAnalysisText(result) {
|
|
7431
7292
|
const lines = [];
|
|
7432
|
-
// Header
|
|
7433
7293
|
lines.push(`Project Analysis: ${result.name}`);
|
|
7434
7294
|
lines.push('='.repeat(30));
|
|
7435
7295
|
lines.push('');
|
|
7436
|
-
// Basic info
|
|
7437
7296
|
lines.push(`Type: ${formatProjectType(result.projectType)}`);
|
|
7438
7297
|
lines.push(`Workspace: ${formatWorkspaceType(result.workspaceType)}`);
|
|
7439
7298
|
lines.push('');
|
|
7440
|
-
// Frameworks
|
|
7441
7299
|
if (result.frameworks.length > 0) {
|
|
7442
7300
|
lines.push('Frameworks:');
|
|
7443
7301
|
for (const framework of result.frameworks) {
|
|
@@ -7451,7 +7309,6 @@ function formatAnalysisText(result) {
|
|
|
7451
7309
|
}
|
|
7452
7310
|
lines.push('');
|
|
7453
7311
|
}
|
|
7454
|
-
// Build tools
|
|
7455
7312
|
if (result.buildTools.length > 0) {
|
|
7456
7313
|
lines.push('Build Tools:');
|
|
7457
7314
|
for (const tool of result.buildTools) {
|
|
@@ -7460,7 +7317,6 @@ function formatAnalysisText(result) {
|
|
|
7460
7317
|
}
|
|
7461
7318
|
lines.push('');
|
|
7462
7319
|
}
|
|
7463
|
-
// Testing
|
|
7464
7320
|
if (result.testingFrameworks.length > 0) {
|
|
7465
7321
|
lines.push('Testing:');
|
|
7466
7322
|
for (const framework of result.testingFrameworks) {
|
|
@@ -7469,7 +7325,6 @@ function formatAnalysisText(result) {
|
|
|
7469
7325
|
}
|
|
7470
7326
|
lines.push('');
|
|
7471
7327
|
}
|
|
7472
|
-
// Entry points
|
|
7473
7328
|
if (result.entryPoints.length > 0) {
|
|
7474
7329
|
lines.push('Entry Points:');
|
|
7475
7330
|
for (const entry of result.entryPoints.slice(0, 5)) {
|
|
@@ -7480,7 +7335,6 @@ function formatAnalysisText(result) {
|
|
|
7480
7335
|
}
|
|
7481
7336
|
lines.push('');
|
|
7482
7337
|
}
|
|
7483
|
-
// Configurations
|
|
7484
7338
|
if (result.configFiles.length > 0) {
|
|
7485
7339
|
lines.push('Configurations:');
|
|
7486
7340
|
for (const config of result.configFiles.slice(0, 8)) {
|
|
@@ -7491,7 +7345,6 @@ function formatAnalysisText(result) {
|
|
|
7491
7345
|
}
|
|
7492
7346
|
lines.push('');
|
|
7493
7347
|
}
|
|
7494
|
-
// Dependencies summary
|
|
7495
7348
|
lines.push('Dependencies:');
|
|
7496
7349
|
lines.push(` Production: ${result.dependencies.production}`);
|
|
7497
7350
|
lines.push(` Development: ${result.dependencies.development}`);
|
|
@@ -7578,7 +7431,7 @@ function parseAnalyzeArgs(args) {
|
|
|
7578
7431
|
exclude: { type: 'string', short: 'e' },
|
|
7579
7432
|
},
|
|
7580
7433
|
allowPositionals: true,
|
|
7581
|
-
strict: false,
|
|
7434
|
+
strict: false,
|
|
7582
7435
|
});
|
|
7583
7436
|
const format = values.format;
|
|
7584
7437
|
const depth = values.depth;
|
|
@@ -7637,7 +7490,6 @@ const analyzeCommandDef = {
|
|
|
7637
7490
|
description: 'Analyze project structure and tech stack',
|
|
7638
7491
|
execute(args, globalOptions) {
|
|
7639
7492
|
const options = parseAnalyzeArgs(args);
|
|
7640
|
-
// Global --json flag overrides format
|
|
7641
7493
|
if (globalOptions.json) {
|
|
7642
7494
|
options.format = 'json';
|
|
7643
7495
|
}
|
|
@@ -7944,12 +7796,10 @@ Examples:
|
|
|
7944
7796
|
function formatDependencyList(deps, maxItems = 20) {
|
|
7945
7797
|
const lines = [];
|
|
7946
7798
|
const depEntries = entries(deps);
|
|
7947
|
-
// Sort alphabetically
|
|
7948
7799
|
depEntries.sort((a, b) => a[0].localeCompare(b[0]));
|
|
7949
7800
|
const displayCount = min(depEntries.length, maxItems);
|
|
7950
7801
|
for (let i = 0; i < displayCount; i++) {
|
|
7951
7802
|
const [name, version] = depEntries[i];
|
|
7952
|
-
// Pad name to align versions
|
|
7953
7803
|
const paddedName = name.padEnd(30);
|
|
7954
7804
|
lines.push(` ${paddedName} ${version}`);
|
|
7955
7805
|
}
|
|
@@ -8192,13 +8042,10 @@ function buildTree(rootPath, walkEntries, options) {
|
|
|
8192
8042
|
isDirectory: true,
|
|
8193
8043
|
children: [],
|
|
8194
8044
|
};
|
|
8195
|
-
// Create a map for quick lookup
|
|
8196
8045
|
const nodeMap = createMap();
|
|
8197
8046
|
nodeMap.set('.', root);
|
|
8198
|
-
// Sort entries by path for proper parent-child relationships
|
|
8199
8047
|
const sortedEntries = [...walkEntries].sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
8200
8048
|
for (const entry of sortedEntries) {
|
|
8201
|
-
// Skip based on filters
|
|
8202
8049
|
if (options.dirsOnly && !entry.isDirectory)
|
|
8203
8050
|
continue;
|
|
8204
8051
|
if (options.filesOnly && entry.isDirectory)
|
|
@@ -8209,7 +8056,6 @@ function buildTree(rootPath, walkEntries, options) {
|
|
|
8209
8056
|
isDirectory: entry.isDirectory,
|
|
8210
8057
|
children: [],
|
|
8211
8058
|
};
|
|
8212
|
-
// Add size/modified if requested
|
|
8213
8059
|
if ((options.showSize || options.showModified) && entry.isFile) {
|
|
8214
8060
|
const stats = getFileStat(entry.path);
|
|
8215
8061
|
if (stats) {
|
|
@@ -8219,9 +8065,8 @@ function buildTree(rootPath, walkEntries, options) {
|
|
|
8219
8065
|
node.modified = stats.modified;
|
|
8220
8066
|
}
|
|
8221
8067
|
}
|
|
8222
|
-
// Find parent
|
|
8223
8068
|
const parts = entry.relativePath.split('/');
|
|
8224
|
-
parts.pop();
|
|
8069
|
+
parts.pop();
|
|
8225
8070
|
const parentPath = parts.join('/') || '.';
|
|
8226
8071
|
const parent = nodeMap.get(parentPath);
|
|
8227
8072
|
if (parent) {
|
|
@@ -8242,11 +8087,9 @@ function buildTree(rootPath, walkEntries, options) {
|
|
|
8242
8087
|
*/
|
|
8243
8088
|
function renderTreeText(node, options, prefix = '', isLast = true) {
|
|
8244
8089
|
const lines = [];
|
|
8245
|
-
// Current line
|
|
8246
8090
|
const connector = isLast ? '└── ' : '├── ';
|
|
8247
8091
|
const dirMark = node.isDirectory ? '/' : '';
|
|
8248
8092
|
let line = `${prefix}${connector}${node.name}${dirMark}`;
|
|
8249
|
-
// Add size/modified
|
|
8250
8093
|
const meta = [];
|
|
8251
8094
|
if (options.showSize && node.size !== undefined) {
|
|
8252
8095
|
meta.push(formatSize(node.size));
|
|
@@ -8258,10 +8101,8 @@ function renderTreeText(node, options, prefix = '', isLast = true) {
|
|
|
8258
8101
|
line += ` [${meta.join(' ')}]`;
|
|
8259
8102
|
}
|
|
8260
8103
|
lines.push(line);
|
|
8261
|
-
// Process children
|
|
8262
8104
|
const childPrefix = prefix + (isLast ? ' ' : '│ ');
|
|
8263
8105
|
const sortedChildren = [...node.children].sort((a, b) => {
|
|
8264
|
-
// Directories first, then alphabetically
|
|
8265
8106
|
if (a.isDirectory && !b.isDirectory)
|
|
8266
8107
|
return -1;
|
|
8267
8108
|
if (!a.isDirectory && b.isDirectory)
|
|
@@ -8285,9 +8126,7 @@ function renderTreeText(node, options, prefix = '', isLast = true) {
|
|
|
8285
8126
|
*/
|
|
8286
8127
|
function formatTreeText(rootPath, tree, options) {
|
|
8287
8128
|
const lines = [];
|
|
8288
|
-
// Root line
|
|
8289
8129
|
lines.push(node_path.basename(rootPath));
|
|
8290
|
-
// Count stats
|
|
8291
8130
|
let dirCount = 0;
|
|
8292
8131
|
let fileCount = 0;
|
|
8293
8132
|
/**
|
|
@@ -8306,7 +8145,6 @@ function formatTreeText(rootPath, tree, options) {
|
|
|
8306
8145
|
countNodes(child);
|
|
8307
8146
|
}
|
|
8308
8147
|
}
|
|
8309
|
-
// Render children of root
|
|
8310
8148
|
const sortedChildren = [...tree.children].sort((a, b) => {
|
|
8311
8149
|
if (a.isDirectory && !b.isDirectory)
|
|
8312
8150
|
return -1;
|
|
@@ -8320,7 +8158,6 @@ function formatTreeText(rootPath, tree, options) {
|
|
|
8320
8158
|
lines.push(...renderTreeText(child, options, '', isLast));
|
|
8321
8159
|
countNodes(child);
|
|
8322
8160
|
}
|
|
8323
|
-
// Summary
|
|
8324
8161
|
lines.push('');
|
|
8325
8162
|
const dirText = dirCount === 1 ? '1 directory' : `${dirCount} directories`;
|
|
8326
8163
|
const fileText = fileCount === 1 ? '1 file' : `${fileCount} files`;
|
|
@@ -8381,7 +8218,6 @@ function parseTreeArgs(args) {
|
|
|
8381
8218
|
function treeCommand(options) {
|
|
8382
8219
|
const rootPath = options.path ? node_path.resolve(options.path) : process.cwd();
|
|
8383
8220
|
try {
|
|
8384
|
-
// Collect entries via walk
|
|
8385
8221
|
const walkEntries = [];
|
|
8386
8222
|
walkDirectory(rootPath, (entry) => {
|
|
8387
8223
|
walkEntries.push(entry);
|
|
@@ -8391,9 +8227,7 @@ function treeCommand(options) {
|
|
|
8391
8227
|
ignorePatterns: options.ignore,
|
|
8392
8228
|
includeHidden: false,
|
|
8393
8229
|
});
|
|
8394
|
-
// Build tree structure
|
|
8395
8230
|
const tree = buildTree(rootPath, walkEntries, options);
|
|
8396
|
-
// Format output
|
|
8397
8231
|
let output;
|
|
8398
8232
|
if (options.format === 'json') {
|
|
8399
8233
|
output = formatTreeJson(tree);
|
|
@@ -8452,6 +8286,9 @@ Examples:
|
|
|
8452
8286
|
|
|
8453
8287
|
/** Logger for CLI operations */
|
|
8454
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');
|
|
8455
8292
|
/** Library version */
|
|
8456
8293
|
const VERSION = '0.1.0';
|
|
8457
8294
|
/**
|
|
@@ -8467,7 +8304,7 @@ const commands = {
|
|
|
8467
8304
|
* Print general help information.
|
|
8468
8305
|
*/
|
|
8469
8306
|
function printHelp() {
|
|
8470
|
-
log(`
|
|
8307
|
+
output.log(`
|
|
8471
8308
|
project-scope <command> [options]
|
|
8472
8309
|
|
|
8473
8310
|
A tool for analyzing JavaScript/TypeScript project structure and tech stack.
|
|
@@ -8499,7 +8336,7 @@ Examples:
|
|
|
8499
8336
|
* Print CLI version.
|
|
8500
8337
|
*/
|
|
8501
8338
|
function printVersion() {
|
|
8502
|
-
log(`project-scope v${VERSION}`);
|
|
8339
|
+
output.log(`project-scope v${VERSION}`);
|
|
8503
8340
|
}
|
|
8504
8341
|
/**
|
|
8505
8342
|
* Parse global options from command line arguments.
|
|
@@ -8560,24 +8397,24 @@ function run(args) {
|
|
|
8560
8397
|
setGlobalLogLevel('debug');
|
|
8561
8398
|
}
|
|
8562
8399
|
cliLogger.debug('CLI invoked', { args, globalOptions });
|
|
8400
|
+
const commandName = args[0];
|
|
8563
8401
|
if (globalOptions.version) {
|
|
8564
8402
|
printVersion();
|
|
8565
8403
|
return { exitCode: 0 };
|
|
8566
8404
|
}
|
|
8567
|
-
if (globalOptions.help && (args.length === 1 || !commands[
|
|
8405
|
+
if (globalOptions.help && (args.length === 1 || !commands[commandName])) {
|
|
8568
8406
|
printHelp();
|
|
8569
8407
|
return { exitCode: 0 };
|
|
8570
8408
|
}
|
|
8571
|
-
const commandName = args[0];
|
|
8572
8409
|
const command = commands[commandName];
|
|
8573
8410
|
if (!command) {
|
|
8574
8411
|
cliLogger.warn('Unknown command requested', { commandName });
|
|
8575
|
-
error(`Unknown command: ${commandName}`);
|
|
8576
|
-
error('Run "project-scope --help" for usage information.');
|
|
8412
|
+
output.error(`Unknown command: ${commandName}`);
|
|
8413
|
+
output.error('Run "project-scope --help" for usage information.');
|
|
8577
8414
|
return { exitCode: 1, error: `Unknown command: ${commandName}` };
|
|
8578
8415
|
}
|
|
8579
8416
|
if (globalOptions.help) {
|
|
8580
|
-
log(command.getHelp());
|
|
8417
|
+
output.log(command.getHelp());
|
|
8581
8418
|
return { exitCode: 0 };
|
|
8582
8419
|
}
|
|
8583
8420
|
const commandArgs = args.slice(1);
|
|
@@ -8585,11 +8422,11 @@ function run(args) {
|
|
|
8585
8422
|
const result = command.execute(commandArgs, globalOptions);
|
|
8586
8423
|
cliLogger.debug('Command completed', { commandName, exitCode: result.exitCode });
|
|
8587
8424
|
if (result.output) {
|
|
8588
|
-
log(result.output);
|
|
8425
|
+
output.log(result.output);
|
|
8589
8426
|
}
|
|
8590
8427
|
if (result.error) {
|
|
8591
8428
|
cliLogger.error('Command error', { commandName, error: result.error });
|
|
8592
|
-
error(result.error);
|
|
8429
|
+
output.error(result.error);
|
|
8593
8430
|
}
|
|
8594
8431
|
return result;
|
|
8595
8432
|
}
|
|
@@ -8625,25 +8462,22 @@ const BINARY_SIGNATURES = [
|
|
|
8625
8462
|
*/
|
|
8626
8463
|
function detectEncodingInfo(buffer) {
|
|
8627
8464
|
encodingLogger.debug('Detecting encoding info', { bufferSize: buffer.length });
|
|
8628
|
-
// Check for UTF-8 BOM
|
|
8629
8465
|
if (buffer.length >= 3) {
|
|
8630
8466
|
if (buffer[0] === UTF8_BOM_BYTES[0] && buffer[1] === UTF8_BOM_BYTES[1] && buffer[2] === UTF8_BOM_BYTES[2]) {
|
|
8631
8467
|
encodingLogger.debug('Detected UTF-8 BOM');
|
|
8632
8468
|
return { type: 'text', encoding: 'utf-8', hasBom: true };
|
|
8633
8469
|
}
|
|
8634
8470
|
}
|
|
8635
|
-
// Check for UTF-16 BOMs
|
|
8636
8471
|
if (buffer.length >= 2) {
|
|
8637
8472
|
if (buffer[0] === UTF16_BE_BOM_BYTES[0] && buffer[1] === UTF16_BE_BOM_BYTES[1]) {
|
|
8638
8473
|
encodingLogger.debug('Detected UTF-16 BE BOM');
|
|
8639
|
-
return { type: 'text', encoding: 'utf16le', hasBom: true };
|
|
8474
|
+
return { type: 'text', encoding: 'utf16le', hasBom: true };
|
|
8640
8475
|
}
|
|
8641
8476
|
if (buffer[0] === UTF16_LE_BOM_BYTES[0] && buffer[1] === UTF16_LE_BOM_BYTES[1]) {
|
|
8642
8477
|
encodingLogger.debug('Detected UTF-16 LE BOM');
|
|
8643
8478
|
return { type: 'text', encoding: 'utf16le', hasBom: true };
|
|
8644
8479
|
}
|
|
8645
8480
|
}
|
|
8646
|
-
// Check for binary signatures
|
|
8647
8481
|
for (const { signature, description } of BINARY_SIGNATURES) {
|
|
8648
8482
|
if (buffer.length >= signature.length) {
|
|
8649
8483
|
let matches = true;
|
|
@@ -8659,7 +8493,6 @@ function detectEncodingInfo(buffer) {
|
|
|
8659
8493
|
}
|
|
8660
8494
|
}
|
|
8661
8495
|
}
|
|
8662
|
-
// Check for null bytes (usually indicates binary)
|
|
8663
8496
|
const sampleSize = min(buffer.length, 8000);
|
|
8664
8497
|
for (let i = 0; i < sampleSize; i++) {
|
|
8665
8498
|
if (buffer[i] === 0) {
|
|
@@ -8678,22 +8511,18 @@ function detectEncodingInfo(buffer) {
|
|
|
8678
8511
|
*/
|
|
8679
8512
|
function detectEncoding(buffer) {
|
|
8680
8513
|
if (buffer.length >= 3) {
|
|
8681
|
-
// Check for UTF-8 BOM
|
|
8682
8514
|
if (buffer[0] === UTF8_BOM_BYTES[0] && buffer[1] === UTF8_BOM_BYTES[1] && buffer[2] === UTF8_BOM_BYTES[2]) {
|
|
8683
8515
|
return 'utf-8';
|
|
8684
8516
|
}
|
|
8685
8517
|
}
|
|
8686
8518
|
if (buffer.length >= 2) {
|
|
8687
|
-
// Check for UTF-16 LE BOM
|
|
8688
8519
|
if (buffer[0] === UTF16_LE_BOM_BYTES[0] && buffer[1] === UTF16_LE_BOM_BYTES[1]) {
|
|
8689
8520
|
return 'utf16le';
|
|
8690
8521
|
}
|
|
8691
|
-
// Check for UTF-16 BE BOM
|
|
8692
8522
|
if (buffer[0] === UTF16_BE_BOM_BYTES[0] && buffer[1] === UTF16_BE_BOM_BYTES[1]) {
|
|
8693
|
-
return 'utf16le';
|
|
8523
|
+
return 'utf16le';
|
|
8694
8524
|
}
|
|
8695
8525
|
}
|
|
8696
|
-
// Default to UTF-8
|
|
8697
8526
|
return 'utf-8';
|
|
8698
8527
|
}
|
|
8699
8528
|
/**
|
|
@@ -8763,10 +8592,8 @@ function bufferToString(content, encoding) {
|
|
|
8763
8592
|
convertLogger.debug('Using provided encoding', { encoding });
|
|
8764
8593
|
return content.toString(encoding);
|
|
8765
8594
|
}
|
|
8766
|
-
// Auto-detect and convert
|
|
8767
8595
|
const info = detectEncodingInfo(content);
|
|
8768
8596
|
if (info.type === 'text') {
|
|
8769
|
-
// Remove BOM if present
|
|
8770
8597
|
let offset = 0;
|
|
8771
8598
|
if (info.hasBom) {
|
|
8772
8599
|
offset = info.encoding === 'utf-8' ? 3 : 2;
|
|
@@ -8819,19 +8646,14 @@ function detectCaseSensitivity() {
|
|
|
8819
8646
|
if (cachedCaseSensitive !== null) {
|
|
8820
8647
|
return cachedCaseSensitive;
|
|
8821
8648
|
}
|
|
8822
|
-
// Quick check based on platform
|
|
8823
8649
|
if (process.platform === 'win32') {
|
|
8824
8650
|
cachedCaseSensitive = false;
|
|
8825
8651
|
return false;
|
|
8826
8652
|
}
|
|
8827
|
-
// macOS is typically case-insensitive by default
|
|
8828
8653
|
if (process.platform === 'darwin') {
|
|
8829
|
-
// Could be case-sensitive HFS+/APFS, but assume insensitive by default
|
|
8830
8654
|
cachedCaseSensitive = false;
|
|
8831
8655
|
return false;
|
|
8832
8656
|
}
|
|
8833
|
-
// Test actual file system behavior for Linux and others
|
|
8834
|
-
// Use mkdtempSync to create a secure temporary directory
|
|
8835
8657
|
let secureTestDir = null;
|
|
8836
8658
|
try {
|
|
8837
8659
|
secureTestDir = node_fs.mkdtempSync(node_path.join(node_os.tmpdir(), 'case-sensitivity-test-'));
|
|
@@ -8842,12 +8664,9 @@ function detectCaseSensitivity() {
|
|
|
8842
8664
|
node_fs.unlinkSync(testFile);
|
|
8843
8665
|
}
|
|
8844
8666
|
catch {
|
|
8845
|
-
// Default to case-sensitive on Linux/Unix if test fails
|
|
8846
|
-
// (win32 and darwin already returned early, so we're on a case-sensitive platform)
|
|
8847
8667
|
cachedCaseSensitive = true;
|
|
8848
8668
|
}
|
|
8849
8669
|
finally {
|
|
8850
|
-
// Clean up the secure temporary directory
|
|
8851
8670
|
if (secureTestDir) {
|
|
8852
8671
|
try {
|
|
8853
8672
|
node_fs.rmdirSync(secureTestDir);
|
|
@@ -8982,7 +8801,6 @@ function normalizeLineEndings(content, style = 'lf') {
|
|
|
8982
8801
|
else {
|
|
8983
8802
|
target = style === 'crlf' ? CRLF : LF;
|
|
8984
8803
|
}
|
|
8985
|
-
// First normalize all to LF, then convert to target
|
|
8986
8804
|
const normalized = content.replace(/\r\n/g, LF).replace(/\r/g, LF);
|
|
8987
8805
|
if (target === LF) {
|
|
8988
8806
|
return normalized;
|
|
@@ -8997,7 +8815,6 @@ function normalizeLineEndings(content, style = 'lf') {
|
|
|
8997
8815
|
*/
|
|
8998
8816
|
function detectLineEnding(content) {
|
|
8999
8817
|
const hasCRLF = content.includes(CRLF);
|
|
9000
|
-
// Check for LF that is NOT part of CRLF
|
|
9001
8818
|
const hasLFOnly = content.includes('\n') && content.replace(/\r\n/g, '').includes('\n');
|
|
9002
8819
|
if (hasCRLF && hasLFOnly)
|
|
9003
8820
|
return 'mixed';
|
|
@@ -9475,7 +9292,6 @@ function createFsTree(root, options) {
|
|
|
9475
9292
|
}
|
|
9476
9293
|
const prefix = normalPath === '.' || normalPath === '' ? '' : normalPath + '/';
|
|
9477
9294
|
for (const [changedPath, change] of _changes) {
|
|
9478
|
-
// Handle root-level files
|
|
9479
9295
|
if (prefix === '') {
|
|
9480
9296
|
const childName = changedPath.split('/')[0];
|
|
9481
9297
|
if (change.type === 'DELETE' && !changedPath.includes('/')) {
|
|
@@ -9492,7 +9308,6 @@ function createFsTree(root, options) {
|
|
|
9492
9308
|
const relativePath = changedPath.slice(prefix.length);
|
|
9493
9309
|
const childName = relativePath.split('/')[0];
|
|
9494
9310
|
if (change.type === 'DELETE') {
|
|
9495
|
-
// Only remove if it's a direct child being deleted
|
|
9496
9311
|
if (!relativePath.includes('/')) {
|
|
9497
9312
|
childSet.delete(childName);
|
|
9498
9313
|
}
|
|
@@ -9578,12 +9393,10 @@ const factoryLogger = createScopedLogger('project-scope:vfs:factory');
|
|
|
9578
9393
|
function createTree(root, options) {
|
|
9579
9394
|
const normalizedRoot = normalizePath(root);
|
|
9580
9395
|
factoryLogger.debug('createTree', { root: normalizedRoot });
|
|
9581
|
-
// Validate root exists
|
|
9582
9396
|
if (!exists(normalizedRoot)) {
|
|
9583
9397
|
factoryLogger.warn('createTree failed: root does not exist', { root: normalizedRoot });
|
|
9584
9398
|
throw createError(`Root directory does not exist: ${normalizedRoot}`);
|
|
9585
9399
|
}
|
|
9586
|
-
// Validate root is a directory
|
|
9587
9400
|
if (!isDirectory(normalizedRoot)) {
|
|
9588
9401
|
factoryLogger.warn('createTree failed: root is not a directory', { root: normalizedRoot });
|
|
9589
9402
|
throw createError(`Root path is not a directory: ${normalizedRoot}`);
|
|
@@ -9630,7 +9443,6 @@ const vfsLogger = createScopedLogger('project-scope:vfs');
|
|
|
9630
9443
|
function commitChanges(tree, options) {
|
|
9631
9444
|
const changes = tree.listChanges();
|
|
9632
9445
|
const appliedChanges = [];
|
|
9633
|
-
// Set log level based on verbose option
|
|
9634
9446
|
if (options?.verbose) {
|
|
9635
9447
|
vfsLogger.setLogLevel('debug');
|
|
9636
9448
|
}
|
|
@@ -9642,7 +9454,6 @@ function commitChanges(tree, options) {
|
|
|
9642
9454
|
changes: [],
|
|
9643
9455
|
dryRun: options?.dryRun ?? false,
|
|
9644
9456
|
};
|
|
9645
|
-
// Dry run - just count changes without writing
|
|
9646
9457
|
if (options?.dryRun) {
|
|
9647
9458
|
for (const change of changes) {
|
|
9648
9459
|
switch (change.type) {
|
|
@@ -9660,7 +9471,6 @@ function commitChanges(tree, options) {
|
|
|
9660
9471
|
result.changes = changes;
|
|
9661
9472
|
return result;
|
|
9662
9473
|
}
|
|
9663
|
-
// Sort changes: deletes first (to free names), then creates, then updates
|
|
9664
9474
|
const sortedChanges = [...changes].sort((a, b) => {
|
|
9665
9475
|
const order = { DELETE: 0, CREATE: 1, UPDATE: 2 };
|
|
9666
9476
|
return order[a.type] - order[b.type];
|
|
@@ -9673,11 +9483,9 @@ function commitChanges(tree, options) {
|
|
|
9673
9483
|
case 'UPDATE':
|
|
9674
9484
|
/* istanbul ignore if -- content is always defined for CREATE/UPDATE from tree.write() */
|
|
9675
9485
|
if (change.content !== undefined) {
|
|
9676
|
-
// Ensure directory exists
|
|
9677
9486
|
const dir = node_path.dirname(absPath);
|
|
9678
9487
|
ensureDir(dir);
|
|
9679
9488
|
node_fs.writeFileSync(absPath, change.content);
|
|
9680
|
-
// Apply permissions if specified
|
|
9681
9489
|
if (change.mode !== undefined) {
|
|
9682
9490
|
node_fs.chmodSync(absPath, change.mode);
|
|
9683
9491
|
}
|
|
@@ -9695,7 +9503,6 @@ function commitChanges(tree, options) {
|
|
|
9695
9503
|
node_fs.unlinkSync(absPath);
|
|
9696
9504
|
}
|
|
9697
9505
|
catch {
|
|
9698
|
-
// Try recursive delete for directories
|
|
9699
9506
|
node_fs.rmSync(absPath, { recursive: true });
|
|
9700
9507
|
}
|
|
9701
9508
|
}
|
|
@@ -9709,7 +9516,6 @@ function commitChanges(tree, options) {
|
|
|
9709
9516
|
}
|
|
9710
9517
|
}
|
|
9711
9518
|
catch (error) {
|
|
9712
|
-
// On error, throw with context
|
|
9713
9519
|
vfsLogger.error('Commit failed', { path: change.path, type: change.type });
|
|
9714
9520
|
const message = error instanceof Error
|
|
9715
9521
|
? `Failed to ${change.type.toLowerCase()} ${change.path}: ${error.message}`
|
|
@@ -9718,7 +9524,6 @@ function commitChanges(tree, options) {
|
|
|
9718
9524
|
}
|
|
9719
9525
|
}
|
|
9720
9526
|
result.changes = appliedChanges;
|
|
9721
|
-
// Clear the tree's pending changes after successful commit
|
|
9722
9527
|
tree.clearChanges();
|
|
9723
9528
|
return result;
|
|
9724
9529
|
}
|
|
@@ -9794,18 +9599,14 @@ function backtrackLcs(table, oldLines, newLines) {
|
|
|
9794
9599
|
* @returns Filtered DiffLine array
|
|
9795
9600
|
*/
|
|
9796
9601
|
function operationsToDiffLines(operations, contextLines) {
|
|
9797
|
-
// Mark which lines should be included (changes + context)
|
|
9798
9602
|
const include = new Array(operations.length).fill(false);
|
|
9799
|
-
// First pass: mark all changes
|
|
9800
9603
|
for (let i = 0; i < operations.length; i++) {
|
|
9801
9604
|
if (operations[i].type !== 'same') {
|
|
9802
|
-
// Mark this line and context around it
|
|
9803
9605
|
for (let j = max(0, i - contextLines); j <= min(operations.length - 1, i + contextLines); j++) {
|
|
9804
9606
|
include[j] = true;
|
|
9805
9607
|
}
|
|
9806
9608
|
}
|
|
9807
9609
|
}
|
|
9808
|
-
// Second pass: convert to DiffLine
|
|
9809
9610
|
const lines = [];
|
|
9810
9611
|
let oldLineNum = 1;
|
|
9811
9612
|
let newLineNum = 1;
|
|
@@ -9827,7 +9628,6 @@ function operationsToDiffLines(operations, contextLines) {
|
|
|
9827
9628
|
}
|
|
9828
9629
|
}
|
|
9829
9630
|
else {
|
|
9830
|
-
// Skip but update line numbers
|
|
9831
9631
|
if (op.type === 'same') {
|
|
9832
9632
|
oldLineNum++;
|
|
9833
9633
|
newLineNum++;
|
|
@@ -9854,7 +9654,6 @@ function bufferToLines(content) {
|
|
|
9854
9654
|
if (!content)
|
|
9855
9655
|
return [];
|
|
9856
9656
|
const text = content.toString('utf-8');
|
|
9857
|
-
// Split by newline, keeping empty last line if present
|
|
9858
9657
|
return text.split('\n');
|
|
9859
9658
|
}
|
|
9860
9659
|
/**
|
|
@@ -9881,9 +9680,7 @@ function generateDiff(change, options = {}) {
|
|
|
9881
9680
|
diffLogger.debug('generateDiff', { path: change.path, type: change.type, contextLines });
|
|
9882
9681
|
const oldLines = bufferToLines(change.originalContent);
|
|
9883
9682
|
const newLines = bufferToLines(change.content);
|
|
9884
|
-
// Handle edge cases
|
|
9885
9683
|
if (change.type === 'CREATE') {
|
|
9886
|
-
// All lines are additions
|
|
9887
9684
|
const lines = newLines
|
|
9888
9685
|
.filter((line) => line !== '' || newLines.indexOf(line) !== newLines.length - 1 || newLines.length === 1)
|
|
9889
9686
|
.map((content, idx) => ({
|
|
@@ -9891,7 +9688,6 @@ function generateDiff(change, options = {}) {
|
|
|
9891
9688
|
line: idx + 1,
|
|
9892
9689
|
content,
|
|
9893
9690
|
}));
|
|
9894
|
-
// Filter out empty trailing line from split
|
|
9895
9691
|
const filteredLines = lines.filter((l, i) => !(i === lines.length - 1 && l.content === '' && lines.length > 1));
|
|
9896
9692
|
return {
|
|
9897
9693
|
path: change.path,
|
|
@@ -9901,13 +9697,11 @@ function generateDiff(change, options = {}) {
|
|
|
9901
9697
|
};
|
|
9902
9698
|
}
|
|
9903
9699
|
if (change.type === 'DELETE') {
|
|
9904
|
-
// All lines are deletions
|
|
9905
9700
|
const lines = oldLines.map((content, idx) => ({
|
|
9906
9701
|
type: 'remove',
|
|
9907
9702
|
line: idx + 1,
|
|
9908
9703
|
content,
|
|
9909
9704
|
}));
|
|
9910
|
-
// Filter out empty trailing line from split
|
|
9911
9705
|
const filteredLines = lines.filter((l, i) => !(i === lines.length - 1 && l.content === '' && lines.length > 1));
|
|
9912
9706
|
return {
|
|
9913
9707
|
path: change.path,
|
|
@@ -9916,7 +9710,6 @@ function generateDiff(change, options = {}) {
|
|
|
9916
9710
|
deletions: filteredLines.length,
|
|
9917
9711
|
};
|
|
9918
9712
|
}
|
|
9919
|
-
// UPDATE: compute actual diff
|
|
9920
9713
|
const table = computeLcsTable(oldLines, newLines);
|
|
9921
9714
|
const operations = backtrackLcs(table, oldLines, newLines);
|
|
9922
9715
|
const lines = operationsToDiffLines(operations, contextLines);
|
|
@@ -9950,13 +9743,11 @@ function generateDiff(change, options = {}) {
|
|
|
9950
9743
|
*/
|
|
9951
9744
|
function formatUnifiedDiff(diff) {
|
|
9952
9745
|
const lines = [];
|
|
9953
|
-
// Header
|
|
9954
9746
|
lines.push(`--- a/${diff.path}`);
|
|
9955
9747
|
lines.push(`+++ b/${diff.path}`);
|
|
9956
9748
|
if (diff.lines.length === 0) {
|
|
9957
9749
|
return lines.join('\n');
|
|
9958
9750
|
}
|
|
9959
|
-
// Group lines into hunks
|
|
9960
9751
|
const hunks = [];
|
|
9961
9752
|
const currentHunk = [];
|
|
9962
9753
|
for (const line of diff.lines) {
|
|
@@ -9965,9 +9756,7 @@ function formatUnifiedDiff(diff) {
|
|
|
9965
9756
|
if (currentHunk.length > 0) {
|
|
9966
9757
|
hunks.push(currentHunk);
|
|
9967
9758
|
}
|
|
9968
|
-
// Output hunks
|
|
9969
9759
|
for (const hunk of hunks) {
|
|
9970
|
-
// Calculate hunk header
|
|
9971
9760
|
const contextAndRemove = hunk.filter((l) => l.type === 'context' || l.type === 'remove');
|
|
9972
9761
|
const contextAndAdd = hunk.filter((l) => l.type === 'context' || l.type === 'add');
|
|
9973
9762
|
const oldStart = hunk.find((l) => l.type === 'context' || l.type === 'remove')?.line ?? 1;
|
|
@@ -9975,7 +9764,6 @@ function formatUnifiedDiff(diff) {
|
|
|
9975
9764
|
const oldCount = contextAndRemove.length;
|
|
9976
9765
|
const newCount = contextAndAdd.length;
|
|
9977
9766
|
lines.push(`@@ -${oldStart},${oldCount} +${newStart},${newCount} @@`);
|
|
9978
|
-
// Output lines
|
|
9979
9767
|
for (const line of hunk) {
|
|
9980
9768
|
const prefix = line.type === 'add' ? '+' : line.type === 'remove' ? '-' : ' ';
|
|
9981
9769
|
lines.push(`${prefix}${line.content}`);
|