pathlra-aliaser 4.6.11 → 4.6.18

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.
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
 
3
3
  /**
4
- * pathlra-aliaser v4.6.11
4
+ * pathlra-aliaser
5
5
  *
6
6
  * Ultra-fast, high-performance path alias resolver and module loader enhancer
7
7
  * Developed by hub-mgv with extreme focus on speed, security, and developer experience
@@ -53,558 +53,579 @@
53
53
  * Requested: "@src/utils/helper"
54
54
  * Matched alias: "@src" → resolves to "/project/src"
55
55
  * Final path: "/project/src/utils/helper"
56
- *
57
- * Changelog v4.6.10:
58
- * - Fixed radix tree alias accumulation bug affecting projects with 100+ aliases
59
- *
60
- * Changelog v4.6.11:
61
- * - Fixed node_modules interference causing incorrect path resolution for nested requires
62
- * - Added parent path check to skip alias resolution inside node_modules
63
56
  */
64
57
 
65
- const p = require("path");
66
- const m = require("module");
67
- const f = require("fs");
68
- const { performance: perf } = require("perf_hooks");
58
+ const path = require("path");
59
+ const moduleLib = require("module");
60
+ const fs = require("fs");
61
+ const { performance: performance } = require("perf_hooks");
69
62
 
70
63
  // Platform-agnostic path separator handling
71
- var s = p.sep;
72
- var sc = s.charCodeAt(0);
73
- var f_sl = 47; // Forward slash code
74
- var b_sl = 92; // Backslash code
75
- var nul = "\0"; // Null separator for cache keys
76
- var csz = 10000; // Max LRU cache size
77
- var ev_b = Math.floor(csz * 0.1); // Eviction batch size
78
- var lin = 0; // Strategy ID: linear scan
79
- var rdx = 1; // Strategy ID: radix tree
80
- let strat = lin; // Current active strategy
64
+ var pathSeparator = path.sep;
65
+ var separatorCode = pathSeparator.charCodeAt(0);
66
+ var forwardSlashCode = 47; // Forward slash code
67
+ var backSlashCode = 92; // Backslash code
68
+ var nullSeparator = "\0"; // Null separator for cache keys
69
+ var cacheMaxSize = 10000; // Max LRU cache size
70
+ var evictionBatchSize = Math.floor(cacheMaxSize * 0.1); // Eviction batch size
71
+ var STRATEGY_LINEAR = 0; // Strategy ID: linear scan
72
+ var STRATEGY_RADIX = 1; // Strategy ID: radix tree
73
+ let currentStrategy = STRATEGY_LINEAR; // Current active strategy
81
74
 
82
75
  // Developer experience flags
83
- let dbg = false; // Debug/verbose mode
84
- let hrld = false; // Hot-reload enabled
85
- let minMode = false; // Minimal footprint mode (<10 aliases)
76
+ let debugMode = false; // Debug/verbose mode
77
+ let hotReloadEnabled = false; // Hot-reload enabled
78
+ let minimalMode = false; // Minimal footprint mode (<10 aliases)
86
79
 
87
80
  /**
88
81
  * Lightweight LRU cache with batch eviction
89
82
  * Optimized for high-frequency module resolution
90
83
  */
91
- class lru {
84
+ class LRUCache {
92
85
  constructor(max) {
93
86
  this.max = max;
94
- this.m = new Map();
95
- this.h = null;
96
- this.t = null;
87
+ this.cacheMap = new Map(); // Key -> node
88
+ this.head = null; // Head (most recently used)
89
+ this.tail = null; // Tail (least recently used)
97
90
  }
98
- get(k) {
99
- const n = this.m.get(k);
100
- if (!n) return undefined;
101
- if (n !== this.h) {
102
- if (n.prev) n.prev.next = n.next;
103
- if (n.next) n.next.prev = n.prev;
104
- if (n === this.t) this.t = n.prev;
105
- n.prev = null;
106
- n.next = this.h;
107
- if (this.h) this.h.prev = n;
108
- this.h = n;
91
+ get(key) {
92
+ const node = this.cacheMap.get(key);
93
+ if (!node) return undefined;
94
+ if (node !== this.head) {
95
+ if (node.prev) node.prev.next = node.next;
96
+ if (node.next) node.next.prev = node.prev;
97
+ if (node === this.tail) this.tail = node.prev;
98
+ node.prev = null;
99
+ node.next = this.head;
100
+ if (this.head) this.head.prev = node;
101
+ this.head = node;
109
102
  }
110
- return n.v;
103
+ return node.value;
111
104
  }
112
- set(k, v) {
113
- let n = this.m.get(k);
114
- if (n) {
115
- n.v = v;
116
- this.get(k);
105
+ set(key, value) {
106
+ let node = this.cacheMap.get(key);
107
+ if (node) {
108
+ node.value = value;
109
+ this.get(key);
117
110
  return;
118
111
  }
119
- n = { k, v, prev: null, next: this.h };
120
- if (this.h) this.h.prev = n;
121
- this.h = n;
122
- if (!this.t) this.t = n;
123
- this.m.set(k, n);
124
- if (this.m.size > this.max) this.evt();
112
+ node = { key, value, prev: null, next: this.head };
113
+ if (this.head) this.head.prev = node;
114
+ this.head = node;
115
+ if (!this.tail) this.tail = node;
116
+ this.cacheMap.set(key, node);
117
+ if (this.cacheMap.size > this.max) this.evict();
125
118
  }
126
- evt() {
127
- if (!this.t) return;
128
- let c = this.t;
129
- for (let i = 0; i < ev_b && c; i++) {
130
- this.m.delete(c.k);
131
- c = c.prev;
119
+ evict() {
120
+ if (!this.tail) return;
121
+ let currentNode = this.tail;
122
+ for (let i = 0; i < evictionBatchSize && currentNode; i++) {
123
+ this.cacheMap.delete(currentNode.key);
124
+ currentNode = currentNode.prev;
132
125
  }
133
- if (c) {
134
- c.next = null;
135
- this.t = c;
126
+ if (currentNode) {
127
+ currentNode.next = null;
128
+ this.tail = currentNode;
136
129
  } else {
137
- this.h = null;
138
- this.t = null;
130
+ this.head = null;
131
+ this.tail = null;
139
132
  }
140
133
  }
141
- clr() {
142
- this.m.clear();
143
- this.h = null;
144
- this.t = null;
134
+ clear() {
135
+ this.cacheMap.clear();
136
+ this.head = null;
137
+ this.tail = null;
145
138
  }
146
139
  }
147
140
 
148
- const rc = new lru(csz);
141
+ // Global resolution cache
142
+ const resolutionCache = new LRUCache(cacheMaxSize);
149
143
 
150
- class rn {
144
+ /**
145
+ * Radix tree node for path prefix matching
146
+ */
147
+ class RadixNode {
151
148
  constructor() {
152
- this.c = null;
153
- this.t = null;
154
- this.e = "";
155
- this.l = false;
149
+ this.children = null;
150
+ this.target = null;
151
+ this.edge = "";
152
+ this.isLeaf = false;
156
153
  }
157
154
  }
158
155
 
159
- class rt {
156
+
157
+
158
+
159
+ /**
160
+ * Radix tree for efficient prefix-based alias lookup
161
+ */
162
+ class RadixTree {
160
163
  constructor() {
161
- this.r = new rn();
164
+ this.root = new RadixNode();
162
165
  }
163
166
 
164
- ins(a, t) {
165
- let n = this.r;
167
+ insert(alias, target) {
168
+ let node = this.root;
166
169
  let i = 0;
167
- const al = a.length;
168
-
169
- while (i < al) {
170
- const cc = a.charCodeAt(i);
171
- if (!n.c) n.c = Object.create(null);
172
- let ch = n.c[cc];
173
- if (!ch) {
174
- ch = new rn();
175
- ch.e = a.slice(i);
176
- ch.t = t;
177
- ch.l = true;
178
- n.c[cc] = ch;
170
+ const aliasLength = alias.length;
171
+
172
+ while (i < aliasLength) {
173
+ const currentChar = alias.charCodeAt(i);
174
+ if (!node.children) node.children = Object.create(null);
175
+ let child = node.children[currentChar];
176
+ if (!child) {
177
+ child = new RadixNode();
178
+ child.edge = alias.slice(i);
179
+ child.target = target;
180
+ child.isLeaf = true;
181
+ node.children[currentChar] = child;
179
182
  return;
180
183
  }
181
184
 
182
- const ed = ch.e;
185
+ const edgeData = child.edge;
183
186
  let j = 0;
184
- const el = ed.length;
185
- const rem = al - i;
186
- while (j < el && j < rem && ed.charCodeAt(j) === a.charCodeAt(i + j)) j++;
187
+ const edgeLength = edgeData.length;
188
+ const remaining = aliasLength - i;
189
+ while (j < edgeLength && j < remaining && edgeData.charCodeAt(j) === alias.charCodeAt(i + j)) j++;
187
190
 
188
- if (j === el) {
189
- i += el;
190
- n = ch;
191
+ if (j === edgeLength) {
192
+ i += edgeLength;
193
+ node = child;
191
194
  continue;
192
195
  }
193
196
 
194
197
  if (j > 0) {
195
- const sp = new rn();
196
- sp.e = ed.slice(0, j);
197
- sp.c = Object.create(null);
198
- ch.e = ed.slice(j);
199
- const es = ed.charCodeAt(j);
200
- sp.c[es] = ch;
201
- const nl = new rn();
202
- nl.e = a.slice(i + j);
203
- nl.t = t;
204
- nl.l = true;
205
- const ns = a.charCodeAt(i + j);
206
- sp.c[ns] = nl;
207
- n.c[cc] = sp;
198
+ const splitNode = new RadixNode();
199
+ splitNode.edge = edgeData.slice(0, j);
200
+ splitNode.children = Object.create(null);
201
+ child.edge = edgeData.slice(j);
202
+ const edgeSplitChar = edgeData.charCodeAt(j);
203
+ splitNode.children[edgeSplitChar] = child;
204
+ const newLeaf = new RadixNode();
205
+ newLeaf.edge = alias.slice(i + j);
206
+ newLeaf.target = target;
207
+ newLeaf.isLeaf = true;
208
+ const newSplitChar = alias.charCodeAt(i + j);
209
+ splitNode.children[newSplitChar] = newLeaf;
210
+ node.children[currentChar] = splitNode;
208
211
  return;
209
212
  }
210
213
 
211
- const br = new rn();
212
- br.c = Object.create(null);
213
- const es0 = ed.charCodeAt(0);
214
- br.c[es0] = ch;
215
- const nl2 = new rn();
216
- nl2.e = a.slice(i);
217
- nl2.t = t;
218
- nl2.l = true;
219
- const ns2 = a.charCodeAt(i);
220
- br.c[ns2] = nl2;
221
- n.c[cc] = br;
214
+ const branchNode = new RadixNode();
215
+ branchNode.children = Object.create(null);
216
+ const edgeFirstChar = edgeData.charCodeAt(0);
217
+ branchNode.children[edgeFirstChar] = child;
218
+ const newLeaf2 = new RadixNode();
219
+ newLeaf2.edge = alias.slice(i);
220
+ newLeaf2.target = target;
221
+ newLeaf2.isLeaf = true;
222
+ const newSplitChar2 = alias.charCodeAt(i);
223
+ branchNode.children[newSplitChar2] = newLeaf2;
224
+ node.children[currentChar] = branchNode;
222
225
  return;
223
226
  }
224
- n.t = t;
225
- n.l = true;
227
+ node.target = target;
228
+ node.isLeaf = true;
226
229
  }
227
230
 
228
- fnd(req) {
229
- let n = this.r;
230
- let lm = null;
231
- let d = 0;
232
- let ma = "";
233
- const rl = req.length;
234
- while (d < rl && n) {
235
- if (n.l) {
236
- const nc = req.charCodeAt(d);
237
- if (nc === f_sl || nc === b_sl || nc === sc) {
238
- lm = { a: ma, t: n.t };
231
+ find(request) {
232
+ let node = this.root;
233
+ let lastMatch = null;
234
+ let depth = 0;
235
+ const requestLength = request.length;
236
+ while (depth < requestLength && node) {
237
+ if (node.isLeaf) {
238
+ const nextChar = request.charCodeAt(depth);
239
+ if (nextChar === forwardSlashCode || nextChar === backSlashCode || nextChar === separatorCode) {
240
+ lastMatch = { alias: node.edge, target: node.target };
239
241
  }
240
242
  }
241
- if (!n.c) break;
242
- const cd = req.charCodeAt(d);
243
- const ch = n.c[cd];
244
- if (!ch) break;
245
- const ed = ch.e;
246
- const el = ed.length;
247
- if (req.startsWith(ed, d)) {
248
- ma += ed;
249
- d += el;
250
- if (ch.l && d === rl) return { a: ma, t: ch.t };
251
- n = ch;
243
+ if (!node.children) break;
244
+ const currentDepthChar = request.charCodeAt(depth);
245
+ const child = node.children[currentDepthChar];
246
+ if (!child) break;
247
+ const edgeData = child.edge;
248
+ const edgeLength = edgeData.length;
249
+ if (request.startsWith(edgeData, depth)) {
250
+ depth += edgeLength;
251
+ if (child.isLeaf && depth === requestLength) return { alias: edgeData, target: child.target };
252
+ node = child;
252
253
  continue;
253
254
  }
254
255
  let k = 0;
255
- while (k < el && d + k < rl && ed.charCodeAt(k) === req.charCodeAt(d + k))
256
+ while (k < edgeLength && depth + k < requestLength && edgeData.charCodeAt(k) === request.charCodeAt(depth + k))
256
257
  k++;
257
258
  if (k === 0) break;
258
259
  if (
259
- ch.l &&
260
- (d + k === rl || [f_sl, b_sl, sc].includes(req.charCodeAt(d + k)))
260
+ child.isLeaf &&
261
+ (depth + k === requestLength || [forwardSlashCode, backSlashCode, separatorCode].includes(request.charCodeAt(depth + k)))
261
262
  ) {
262
- return { a: ma + ed.slice(0, k), t: ch.t };
263
+ return { alias: edgeData.slice(0, k), target: child.target };
263
264
  }
264
265
  break;
265
266
  }
266
- return lm;
267
+ return lastMatch;
267
268
  }
268
269
  }
269
270
 
270
- const cp = new Set();
271
- const am = new Map();
272
- const seenAliases = new Set();
273
- let tree = null;
274
- let sa = null;
275
- let pa = [];
276
- let ha = false;
277
- let ac = false;
278
- let pc = false;
271
+ // Global state
272
+ const customPathsSet = new Set(); // Custom paths
273
+ const aliasMap = new Map(); // Aliases
274
+ const seenAliases = new Set(); // For duplicate detection
275
+ let radixTree = null;
276
+ let sortedAliases = null;
277
+ let pathArray = [];
278
+ let hasAliases = false;
279
+ let aliasesChanged = false;
280
+ let pathsChanged = false;
279
281
  let lastPkgPath = null;
280
282
 
281
- const Mod = module.constructor.length > 1 ? module.constructor : m;
282
- const _nmp = Mod._nodeModulePaths;
283
- const _rfn = Mod._resolveFilename;
284
-
283
+ // Patch Node.js module system
284
+ const Module = moduleLib.constructor.length > 1 ? moduleLib.constructor : moduleLib;
285
+ const originalNodeModulePaths = Module._nodeModulePaths;
286
+ const originalResolveFilename = Module._resolveFilename;
285
287
 
286
- Mod._nodeModulePaths = function (frm) {
287
- if (frm.includes(`${s}node_modules${s}`)) return _nmp.call(this, frm);
288
- const ps = _nmp.call(this, frm);
289
- return pa.length ? pa.concat(ps) : ps;
288
+ Module._nodeModulePaths = function (fromPath) {
289
+ if (fromPath.includes(`${pathSeparator}node_modules${pathSeparator}`)) return originalNodeModulePaths.call(this, fromPath);
290
+ const pathsList = originalNodeModulePaths.call(this, fromPath);
291
+ return pathArray.length ? pathArray.concat(pathsList) : pathsList;
290
292
  };
291
293
 
292
-
293
- Mod._resolveFilename = function (req, prnt, isM, opts) {
294
- const pp = prnt?.filename || "";
295
-
296
- if (pp.includes(`${s}node_modules${s}`)) {
297
- return _rfn.call(this, req, prnt, isM, opts);
294
+ Module._resolveFilename = function (request, parent, isMain, options) {
295
+ const parentPath = parent?.filename || "";
296
+ const cacheKey = parentPath + nullSeparator + request;
297
+ const cachedResult = resolutionCache.get(cacheKey);
298
+ if (cachedResult !== undefined) {
299
+ if (debugMode) console.log(`pathlra-aliaser CACHE HIT ${request} → ${cachedResult}`);
300
+ return cachedResult;
298
301
  }
299
302
 
300
- const ck = pp + nul + req;
301
- const ch = rc.get(ck);
302
- if (ch !== undefined) {
303
- if (dbg) console.log(`pathlra-aliaser CACHE HIT ${req} → ${ch}`);
304
- return ch;
305
- }
306
-
307
- let rr = req;
308
- let mr = null;
303
+ let resolvedRequest = request;
304
+ let matchResult = null;
309
305
 
310
- if (ha) {
311
- if (ac) {
312
- opt();
313
- ac = false;
306
+ if (hasAliases) {
307
+ if (aliasesChanged) {
308
+ optimizeStrategy();
309
+ aliasesChanged = false;
314
310
  }
315
311
 
316
- if (strat === lin) {
317
- const rl = req.length;
318
- for (let i = 0; i < sa.length; i++) {
319
- const [a, t] = sa[i];
320
- const al = a.length;
321
- if (al > rl) continue;
322
- if (req.startsWith(a)) {
323
- if (al === rl || [f_sl, b_sl, sc].includes(req.charCodeAt(al))) {
324
- mr = { a, t };
312
+ if (currentStrategy === STRATEGY_LINEAR) {
313
+ const requestLength = request.length;
314
+ for (let i = 0; i < sortedAliases.length; i++) {
315
+ const [alias, target] = sortedAliases[i];
316
+ const aliasLength = alias.length;
317
+ if (aliasLength > requestLength) continue;
318
+ if (request.startsWith(alias)) {
319
+ if (aliasLength === requestLength || [forwardSlashCode, backSlashCode, separatorCode].includes(request.charCodeAt(aliasLength))) {
320
+ matchResult = { alias, target };
325
321
  break;
326
322
  }
327
323
  }
328
324
  }
329
325
  } else {
330
- mr = tree.fnd(req);
326
+ matchResult = radixTree.find(request);
331
327
  }
332
328
 
333
-
334
- if (mr) {
335
- const { a, t } = mr;
336
- const rtg = typeof t === "function" ? t(pp, req, a) : t;
337
- if (typeof rtg !== "string") {
329
+ if (matchResult) {
330
+ const { alias, target } = matchResult;
331
+ const resolvedTarget = typeof target === "function" ? target(parentPath, request, alias) : target;
332
+ if (typeof resolvedTarget !== "string") {
338
333
  throw new Error(
339
- "pathlra-aliaser Custom handler must return string path",
334
+ "pathlra-aliaser Custom handler must return string path"
340
335
  );
341
336
  }
342
- if (!isValidTarget(rtg)) {
343
- throw new Error(`pathlra-aliaser Invalid alias target detected ${rtg}`);
337
+ // SECURITY: Validate target path
338
+ if (!isValidTarget(resolvedTarget)) {
339
+ throw new Error(`pathlra-aliaser Invalid alias target detected ${resolvedTarget}`);
344
340
  }
345
- const sf = req.slice(a.length);
346
- rr = sf
347
- ? rtg +
348
- (sf.charCodeAt(0) === sc || sf.charCodeAt(0) === f_sl ? sf : s + sf)
349
- : rtg;
350
- if (dbg)
351
- console.log(`pathlra-aliaser RESOLVED ${req} → ${rr} (via ${a})`);
352
- }
353
-
354
- else if (dbg) {
355
- console.log(`pathlra-aliaser NO MATCH ${req}`);
341
+ const suffix = request.slice(alias.length);
342
+ resolvedRequest = suffix ? resolvedTarget + (suffix.charCodeAt(0) === separatorCode ? suffix : pathSeparator + suffix) : resolvedTarget;
343
+ if (debugMode)
344
+ console.log(`pathlra-aliaser RESOLVED ${request} ${resolvedRequest} (via ${alias})`);
345
+ } else if (debugMode) {
346
+ console.log(`pathlra-aliaser NO MATCH ${request}`);
356
347
  }
357
348
  }
358
349
 
359
- const res = _rfn.call(this, rr, prnt, isM, opts);
360
- rc.set(ck, res);
361
- return res;
350
+ const result = originalResolveFilename.call(this, resolvedRequest, parent, isMain, options);
351
+ resolutionCache.set(cacheKey, result);
352
+ return result;
362
353
  };
363
354
 
364
- function isValidTarget(t) {
365
- if (t.includes("..")) return false;
366
- if (t.includes("~")) return false;
367
- if (t.includes("\0")) return false;
355
+ /**
356
+ * Validate alias target to prevent path injection
357
+ */
358
+ function isValidTarget(targetPath) {
359
+ if (targetPath.includes("..")) return false;
360
+ if (targetPath.includes("~")) return false;
361
+ if (targetPath.includes("\0")) return false;
368
362
  try {
369
- p.normalize(t);
363
+ path.normalize(targetPath);
370
364
  return true;
371
365
  } catch {
372
366
  return false;
373
367
  }
374
368
  }
375
369
 
376
- function aa(a, t) {
377
- if (seenAliases.has(a)) {
370
+ /**
371
+ * Register single alias with duplicate warning
372
+ */
373
+ function addAlias(alias, target) {
374
+ if (seenAliases.has(alias)) {
375
+ console.warn(
376
+ `pathlra-aliaser WARNING Duplicate alias "${alias}" detected Overwriting`
377
+ );
378
378
  } else {
379
- seenAliases.add(a);
379
+ seenAliases.add(alias);
380
380
  }
381
- am.set(a, t);
382
- ha = true;
383
- ac = true;
381
+ aliasMap.set(alias, target);
382
+ hasAliases = true;
383
+ aliasesChanged = true;
384
384
  }
385
385
 
386
- function ap(d) {
387
- const nd = p.normalize(d);
388
- if (cp.has(nd)) return;
389
- cp.add(nd);
390
- pa = [...cp].sort((x, y) => y.length - x.length);
391
- pc = true;
392
- if (hrld) setImmediate(apc);
386
+ /**
387
+ * Add custom module directory
388
+ */
389
+ function addPath(directory) {
390
+ const normalizedDir = path.normalize(directory);
391
+ if (customPathsSet.has(normalizedDir)) return;
392
+ customPathsSet.add(normalizedDir);
393
+ pathArray = [...customPathsSet].sort((x, y) => y.length - x.length);
394
+ pathsChanged = true;
395
+ if (hotReloadEnabled) setImmediate(applyPathsToCache);
393
396
  }
394
397
 
395
- function apc() {
396
- if (!pc) return;
397
- const mn = require.main;
398
- if (mn && !mn._simulateRepl) ump(mn);
399
- let pr = module.parent;
400
- const sn = new Set();
401
- while (pr && !sn.has(pr)) {
402
- sn.add(pr);
403
- ump(pr);
404
- pr = pr.parent;
398
+ function applyPathsToCache() {
399
+ if (!pathsChanged) return;
400
+ const mainModule = require.main;
401
+ if (mainModule && !mainModule._simulateRepl) updateModulePaths(mainModule);
402
+ let parentModule = module.parent;
403
+ const seenNodes = new Set();
404
+ while (parentModule && !seenNodes.has(parentModule)) {
405
+ seenNodes.add(parentModule);
406
+ updateModulePaths(parentModule);
407
+ parentModule = parentModule.parent;
405
408
  }
406
- pc = false;
409
+ pathsChanged = false;
407
410
  }
408
411
 
409
- function ump(md) {
410
- if (!md.paths) return;
411
- for (const d of cp) {
412
- if (!md.paths.includes(d)) md.paths.unshift(d);
412
+ function updateModulePaths(moduleData) {
413
+ if (!moduleData.paths) return;
414
+ for (const directory of customPathsSet) {
415
+ if (!moduleData.paths.includes(directory)) moduleData.paths.unshift(directory);
413
416
  }
414
417
  }
415
418
 
416
- function opt() {
417
- const cnt = am.size;
418
- if (cnt === 0) {
419
- ha = false;
420
- sa = null;
421
- tree = null;
422
- strat = lin;
423
- minMode = false;
419
+ /**
420
+ * Optimize based on alias count
421
+ */
422
+ function optimizeStrategy() {
423
+ const count = aliasMap.size;
424
+ if (count === 0) {
425
+ hasAliases = false;
426
+ sortedAliases = null;
427
+ radixTree = null;
428
+ currentStrategy = STRATEGY_LINEAR;
429
+ minimalMode = false;
424
430
  return;
425
431
  }
426
432
 
427
- minMode = cnt < 10;
428
- if (minMode) {
429
- rc.max = 1000;
430
- ev_b = 100;
433
+ minimalMode = count < 10;
434
+ if (minimalMode) {
435
+ // Reduce cache size in minimal mode
436
+ resolutionCache.max = 1000;
437
+ evictionBatchSize = 100;
431
438
  } else {
432
- rc.max = csz;
433
- ev_b = Math.floor(csz * 0.1);
439
+ resolutionCache.max = cacheMaxSize;
440
+ evictionBatchSize = Math.floor(cacheMaxSize * 0.1);
434
441
  }
435
442
 
436
- if (cnt < 100) {
437
- strat = lin;
438
- sa = [...am.entries()].sort((x, y) => y[0].length - x[0].length);
439
- tree = null;
443
+ if (count < 100) {
444
+ currentStrategy = STRATEGY_LINEAR;
445
+ sortedAliases = [...aliasMap.entries()].sort((x, y) => y[0].length - x[0].length);
446
+ radixTree = null;
440
447
  } else {
441
- strat = rdx;
442
- bld();
443
- sa = null;
448
+ currentStrategy = STRATEGY_RADIX;
449
+ buildRadixTree();
450
+ sortedAliases = null;
444
451
  }
445
452
  }
446
453
 
447
- function bld() {
448
- tree = new rt();
449
- am.forEach((t, a) => tree.ins(a, t));
454
+ function buildRadixTree() {
455
+ radixTree = new RadixTree();
456
+ aliasMap.forEach((target, alias) => radixTree.insert(alias, target));
450
457
  }
451
458
 
452
- function init(opts = {}) {
453
- const st = perf.now();
454
- const bs = gbp(opts);
455
- const pkg = lpj(bs);
456
- lastPkgPath = p.join(bs, "package.json");
457
-
458
- if (opts.debug) dbg = true;
459
- if (opts.hotReload) hrld = true;
460
-
461
- if (hrld && lastPkgPath) {
462
- f.watch(lastPkgPath, () => {
459
+ /**
460
+ * Initialize from package.json or options
461
+ */
462
+ function initialize(options = {}) {
463
+ const startTime = performance.now();
464
+ const basePath = getBasePath(options);
465
+ const packageJson = loadPackageJson(basePath);
466
+ lastPkgPath = path.join(basePath, "package.json");
467
+
468
+ // Enable debug mode
469
+ if (options.debug) debugMode = true;
470
+ if (options.hotReload) hotReloadEnabled = true;
471
+
472
+ // Auto-watch for changes in dev (hot-reload)
473
+ if (hotReloadEnabled && lastPkgPath) {
474
+ fs.watch(lastPkgPath, () => {
463
475
  console.log("pathlra-aliaser package.json changed. Reloading aliases...");
464
- rst();
465
- init({ base: bs, debug: dbg, hotReload: hrld });
476
+ reset();
477
+ initialize({ base: basePath, debug: debugMode, hotReload: hotReloadEnabled });
466
478
  });
467
479
  }
468
480
 
469
- const cfgKey = Object.keys(pkg).find((k) => k.startsWith("path_aliaser"));
470
- const als = cfgKey ? pkg[cfgKey] : {};
481
+ // Find config section
482
+ const configKey = Object.keys(packageJson).find((k) => k.startsWith("path_aliaser"));
483
+ const aliases = configKey ? packageJson[configKey] : {};
471
484
 
472
- if (Object.keys(als).length === 0) {
473
- als["@root"] = ".";
474
- als["@src"] = "src";
485
+ // Apply default presets if none exist
486
+ if (Object.keys(aliases).length === 0) {
487
+ aliases["@root"] = ".";
488
+ aliases["@src"] = "src";
475
489
  console.log(
476
- "pathlra-aliaser No aliases found. Using defaults: @root → ., @src → src",
490
+ "pathlra-aliaser No aliases found. Using defaults: @root → ., @src → src"
477
491
  );
478
492
  }
479
493
 
480
- for (const [a, t] of Object.entries(als)) {
481
- if (typeof t !== "string" && typeof t !== "function") {
494
+ // Register aliases
495
+ for (const [alias, target] of Object.entries(aliases)) {
496
+ if (typeof target !== "string" && typeof target !== "function") {
482
497
  throw new Error(
483
- `pathlra-aliaser Invalid alias target for "${a}". Must be string or function`,
498
+ `pathlra-aliaser Invalid alias target for "${alias}". Must be string or function`
484
499
  );
485
500
  }
486
- const r = t.startsWith("/") ? t : p.join(bs, t);
487
- aa(a, r);
501
+ const resolvedPath = target.startsWith("/") ? target : path.join(basePath, target);
502
+ addAlias(alias, resolvedPath);
488
503
  }
489
504
 
490
- const dirs = pkg._moduleDirectories || ["node_modules"];
491
- for (const d of dirs) {
492
- if (d !== "node_modules") ap(p.join(bs, d));
505
+ // Custom module directories
506
+ const directories = packageJson._moduleDirectories || ["node_modules"];
507
+ for (const directory of directories) {
508
+ if (directory !== "node_modules") addPath(path.join(basePath, directory));
493
509
  }
494
510
 
495
- opt();
496
- apc();
511
+ optimizeStrategy();
512
+ applyPathsToCache();
497
513
 
498
- const dur = perf.now() - st;
499
- if (dur > 20) {
514
+ const duration = performance.now() - startTime;
515
+ if (duration > 20) {
500
516
  console.warn(
501
- `pathlra-aliaser Init took ${dur.toFixed(1)}ms (optimized for ${
502
- am.size
503
- } aliases)`,
517
+ `pathlra-aliaser Init took ${duration.toFixed(1)}ms (optimized for ${
518
+ aliasMap.size
519
+ } aliases)`
504
520
  );
505
521
  }
506
522
 
507
523
  return {
508
- aliases: am.size,
509
- paths: cp.size,
510
- duration: dur,
511
- minimalMode: minMode,
524
+ aliases: aliasMap.size,
525
+ paths: customPathsSet.size,
526
+ duration: duration,
527
+ minimalMode: minimalMode,
512
528
  };
513
529
  }
514
530
 
515
- function gbp(o) {
516
- if (typeof o === "string") o = { base: o };
517
- if (o.base) return p.resolve(o.base.replace(/\/package\.json$/, ""));
518
- const cands = [p.join(__dirname, "../.."), process.cwd()];
519
- for (const c of cands) {
531
+ function getBasePath(options) {
532
+ if (typeof options === "string") options = { base: options };
533
+ if (options.base) return path.resolve(options.base.replace(/\/package\.json$/, ""));
534
+ const candidates = [path.join(__dirname, "../.."), process.cwd()];
535
+ for (const candidate of candidates) {
520
536
  try {
521
- f.accessSync(p.join(c, "package.json"), f.constants.R_OK);
522
- return c;
537
+ fs.accessSync(path.join(candidate, "package.json"), fs.constants.R_OK);
538
+ return candidate;
523
539
  } catch {}
524
540
  }
525
- throw new Error(`Failed to locate package.json in\n${cands.join("\n")}`);
541
+ throw new Error(`Failed to locate package.json in\n${candidates.join("\n")}`);
526
542
  }
527
543
 
528
- function lpj(b) {
544
+ function loadPackageJson(basePath) {
529
545
  try {
530
- const pp = p.join(b, "package.json");
531
- return JSON.parse(f.readFileSync(pp, "utf8"));
532
- } catch (e) {
533
- throw new Error(`Failed to load package.json: ${e.message}`);
546
+ const packagePath = path.join(basePath, "package.json");
547
+ return JSON.parse(fs.readFileSync(packagePath, "utf8"));
548
+ } catch (error) {
549
+ throw new Error(`Failed to load package.json: ${error.message}`);
534
550
  }
535
551
  }
536
552
 
537
- function rst() {
538
- rc.clr();
539
- cp.clear();
540
- am.clear();
553
+ function reset() {
554
+ resolutionCache.clear();
555
+ customPathsSet.clear();
556
+ aliasMap.clear();
541
557
  seenAliases.clear();
542
- pa = [];
543
- tree = null;
544
- sa = null;
545
- ha = false;
546
- ac = false;
547
- pc = false;
548
- dbg = false;
549
- hrld = false;
550
- minMode = false;
551
-
552
- const mn = require.main;
553
- if (mn && !mn._simulateRepl) cmp(mn);
554
- let pr = module.parent;
555
- const sn = new Set();
556
- while (pr && !sn.has(pr)) {
557
- sn.add(pr);
558
- cmp(pr);
559
- pr = pr.parent;
558
+ pathArray = [];
559
+ radixTree = null;
560
+ sortedAliases = null;
561
+ hasAliases = false;
562
+ aliasesChanged = false;
563
+ pathsChanged = false;
564
+ debugMode = false;
565
+ hotReloadEnabled = false;
566
+ minimalMode = false;
567
+
568
+ const mainModule = require.main;
569
+ if (mainModule && !mainModule._simulateRepl) cleanModulePaths(mainModule);
570
+ let parentModule = module.parent;
571
+ const seenNodes = new Set();
572
+ while (parentModule && !seenNodes.has(parentModule)) {
573
+ seenNodes.add(parentModule);
574
+ cleanModulePaths(parentModule);
575
+ parentModule = parentModule.parent;
560
576
  }
561
- const ps = [...cp];
562
- for (const k of Object.keys(require.cache)) {
563
- if (ps.some((x) => k.startsWith(x))) delete require.cache[k];
577
+ const pathsList = [...customPathsSet];
578
+ for (const key of Object.keys(require.cache)) {
579
+ if (pathsList.some((pathItem) => key.startsWith(pathItem))) delete require.cache[key];
564
580
  }
565
581
  }
566
582
 
567
- function cmp(md) {
568
- if (!md.paths) return;
569
- md.paths = md.paths.filter((x) => !cp.has(x));
583
+ function cleanModulePaths(moduleData) {
584
+ if (!moduleData.paths) return;
585
+ moduleData.paths = moduleData.paths.filter((pathItem) => !customPathsSet.has(pathItem));
570
586
  }
571
587
 
572
- module.exports = Object.assign(init, {
573
- ap,
574
- aa,
575
- addAliases: (als) => {
576
- for (const [a, t] of Object.entries(als)) aa(a, t);
577
- ac = true;
588
+ // Public API
589
+ module.exports = Object.assign(initialize, {
590
+ addPath,
591
+ addAlias,
592
+ addAliases: (aliases) => {
593
+ for (const [alias, target] of Object.entries(aliases)) addAlias(alias, target);
594
+ aliasesChanged = true;
578
595
  },
579
- rst,
596
+ reset,
580
597
  _internal: {
581
598
  getStats: () => ({
582
- aliases: am.size,
583
- paths: cp.size,
584
- cacheSize: rc.m.size,
585
- strategy: strat === lin ? "LINEAR" : "RADIX",
586
- minimalMode: minMode,
587
- hotReload: hrld,
588
- debug: dbg,
599
+ aliases: aliasMap.size,
600
+ paths: customPathsSet.size,
601
+ cacheSize: resolutionCache.cacheMap.size,
602
+ strategy: currentStrategy === STRATEGY_LINEAR ? "LINEAR" : "RADIX",
603
+ minimalMode: minimalMode,
604
+ hotReload: hotReloadEnabled,
605
+ debug: debugMode,
589
606
  memory: (process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2) + " MB",
590
607
  }),
591
- forceStrategy: (st) => {
592
- strat = st;
593
- if (st === rdx) bld();
608
+ forceStrategy: (strategy) => {
609
+ currentStrategy = strategy;
610
+ if (strategy === STRATEGY_RADIX) buildRadixTree();
594
611
  },
595
- clearCache: () => rc.clr(),
612
+ clearCache: () => resolutionCache.clear(),
613
+ /**
614
+ * Generate tsconfig.json paths for TypeScript integration
615
+ * Usage: fs.writeFileSync('tsconfig.json', JSON.stringify(generateTSConfig(), null, 2))
616
+ */
596
617
  generateTSConfig: () => {
597
618
  const compilerOptions = {
598
619
  baseUrl: ".",
599
620
  paths: {},
600
621
  };
601
- am.forEach((target, alias) => {
602
- let rel = p.relative(process.cwd(), target);
603
- if (!rel.startsWith(".")) rel = "./" + rel;
604
- compilerOptions.paths[alias + "/*"] = [rel + "/*"];
605
- compilerOptions.paths[alias] = [rel];
622
+ aliasMap.forEach((target, alias) => {
623
+ let relativePath = path.relative(process.cwd(), target);
624
+ if (!relativePath.startsWith(".")) relativePath = "./" + relativePath;
625
+ compilerOptions.paths[alias + "/*"] = [relativePath + "/*"];
626
+ compilerOptions.paths[alias] = [relativePath];
606
627
  });
607
628
  return { compilerOptions };
608
629
  },
609
630
  },
610
- });
631
+ });