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.
- package/LICENSE +1 -1
- package/index.js +1 -2
- package/package.json +6 -6
- package/pathlra-aliaser.js +398 -377
- package/test/example/package-lock.json +838 -0
package/pathlra-aliaser.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* pathlra-aliaser
|
|
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
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
const { performance:
|
|
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
|
|
72
|
-
var
|
|
73
|
-
var
|
|
74
|
-
var
|
|
75
|
-
var
|
|
76
|
-
var
|
|
77
|
-
var
|
|
78
|
-
var
|
|
79
|
-
var
|
|
80
|
-
let
|
|
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
|
|
84
|
-
let
|
|
85
|
-
let
|
|
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
|
|
84
|
+
class LRUCache {
|
|
92
85
|
constructor(max) {
|
|
93
86
|
this.max = max;
|
|
94
|
-
this.
|
|
95
|
-
this.
|
|
96
|
-
this.
|
|
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(
|
|
99
|
-
const
|
|
100
|
-
if (!
|
|
101
|
-
if (
|
|
102
|
-
if (
|
|
103
|
-
if (
|
|
104
|
-
if (
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (this.
|
|
108
|
-
this.
|
|
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
|
|
103
|
+
return node.value;
|
|
111
104
|
}
|
|
112
|
-
set(
|
|
113
|
-
let
|
|
114
|
-
if (
|
|
115
|
-
|
|
116
|
-
this.get(
|
|
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
|
-
|
|
120
|
-
if (this.
|
|
121
|
-
this.
|
|
122
|
-
if (!this.
|
|
123
|
-
this.
|
|
124
|
-
if (this.
|
|
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
|
-
|
|
127
|
-
if (!this.
|
|
128
|
-
let
|
|
129
|
-
for (let i = 0; i <
|
|
130
|
-
this.
|
|
131
|
-
|
|
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 (
|
|
134
|
-
|
|
135
|
-
this.
|
|
126
|
+
if (currentNode) {
|
|
127
|
+
currentNode.next = null;
|
|
128
|
+
this.tail = currentNode;
|
|
136
129
|
} else {
|
|
137
|
-
this.
|
|
138
|
-
this.
|
|
130
|
+
this.head = null;
|
|
131
|
+
this.tail = null;
|
|
139
132
|
}
|
|
140
133
|
}
|
|
141
|
-
|
|
142
|
-
this.
|
|
143
|
-
this.
|
|
144
|
-
this.
|
|
134
|
+
clear() {
|
|
135
|
+
this.cacheMap.clear();
|
|
136
|
+
this.head = null;
|
|
137
|
+
this.tail = null;
|
|
145
138
|
}
|
|
146
139
|
}
|
|
147
140
|
|
|
148
|
-
|
|
141
|
+
// Global resolution cache
|
|
142
|
+
const resolutionCache = new LRUCache(cacheMaxSize);
|
|
149
143
|
|
|
150
|
-
|
|
144
|
+
/**
|
|
145
|
+
* Radix tree node for path prefix matching
|
|
146
|
+
*/
|
|
147
|
+
class RadixNode {
|
|
151
148
|
constructor() {
|
|
152
|
-
this.
|
|
153
|
-
this.
|
|
154
|
-
this.
|
|
155
|
-
this.
|
|
149
|
+
this.children = null;
|
|
150
|
+
this.target = null;
|
|
151
|
+
this.edge = "";
|
|
152
|
+
this.isLeaf = false;
|
|
156
153
|
}
|
|
157
154
|
}
|
|
158
155
|
|
|
159
|
-
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Radix tree for efficient prefix-based alias lookup
|
|
161
|
+
*/
|
|
162
|
+
class RadixTree {
|
|
160
163
|
constructor() {
|
|
161
|
-
this.
|
|
164
|
+
this.root = new RadixNode();
|
|
162
165
|
}
|
|
163
166
|
|
|
164
|
-
|
|
165
|
-
let
|
|
167
|
+
insert(alias, target) {
|
|
168
|
+
let node = this.root;
|
|
166
169
|
let i = 0;
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
while (i <
|
|
170
|
-
const
|
|
171
|
-
if (!
|
|
172
|
-
let
|
|
173
|
-
if (!
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
|
185
|
+
const edgeData = child.edge;
|
|
183
186
|
let j = 0;
|
|
184
|
-
const
|
|
185
|
-
const
|
|
186
|
-
while (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 ===
|
|
189
|
-
i +=
|
|
190
|
-
|
|
191
|
+
if (j === edgeLength) {
|
|
192
|
+
i += edgeLength;
|
|
193
|
+
node = child;
|
|
191
194
|
continue;
|
|
192
195
|
}
|
|
193
196
|
|
|
194
197
|
if (j > 0) {
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
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
|
|
212
|
-
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
|
|
225
|
-
|
|
227
|
+
node.target = target;
|
|
228
|
+
node.isLeaf = true;
|
|
226
229
|
}
|
|
227
230
|
|
|
228
|
-
|
|
229
|
-
let
|
|
230
|
-
let
|
|
231
|
-
let
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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 (!
|
|
242
|
-
const
|
|
243
|
-
const
|
|
244
|
-
if (!
|
|
245
|
-
const
|
|
246
|
-
const
|
|
247
|
-
if (
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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 <
|
|
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
|
-
|
|
260
|
-
(
|
|
260
|
+
child.isLeaf &&
|
|
261
|
+
(depth + k === requestLength || [forwardSlashCode, backSlashCode, separatorCode].includes(request.charCodeAt(depth + k)))
|
|
261
262
|
) {
|
|
262
|
-
return {
|
|
263
|
+
return { alias: edgeData.slice(0, k), target: child.target };
|
|
263
264
|
}
|
|
264
265
|
break;
|
|
265
266
|
}
|
|
266
|
-
return
|
|
267
|
+
return lastMatch;
|
|
267
268
|
}
|
|
268
269
|
}
|
|
269
270
|
|
|
270
|
-
|
|
271
|
-
const
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
let
|
|
275
|
-
let
|
|
276
|
-
let
|
|
277
|
-
let
|
|
278
|
-
let
|
|
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
|
-
|
|
282
|
-
const
|
|
283
|
-
const
|
|
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
|
-
|
|
287
|
-
if (
|
|
288
|
-
const
|
|
289
|
-
return
|
|
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
|
-
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
if (
|
|
297
|
-
|
|
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
|
-
|
|
301
|
-
|
|
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 (
|
|
311
|
-
if (
|
|
312
|
-
|
|
313
|
-
|
|
306
|
+
if (hasAliases) {
|
|
307
|
+
if (aliasesChanged) {
|
|
308
|
+
optimizeStrategy();
|
|
309
|
+
aliasesChanged = false;
|
|
314
310
|
}
|
|
315
311
|
|
|
316
|
-
if (
|
|
317
|
-
const
|
|
318
|
-
for (let i = 0; i <
|
|
319
|
-
const [
|
|
320
|
-
const
|
|
321
|
-
if (
|
|
322
|
-
if (
|
|
323
|
-
if (
|
|
324
|
-
|
|
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
|
-
|
|
326
|
+
matchResult = radixTree.find(request);
|
|
331
327
|
}
|
|
332
328
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
const
|
|
336
|
-
|
|
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
|
-
|
|
343
|
-
|
|
337
|
+
// SECURITY: Validate target path
|
|
338
|
+
if (!isValidTarget(resolvedTarget)) {
|
|
339
|
+
throw new Error(`pathlra-aliaser Invalid alias target detected ${resolvedTarget}`);
|
|
344
340
|
}
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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
|
|
360
|
-
|
|
361
|
-
return
|
|
350
|
+
const result = originalResolveFilename.call(this, resolvedRequest, parent, isMain, options);
|
|
351
|
+
resolutionCache.set(cacheKey, result);
|
|
352
|
+
return result;
|
|
362
353
|
};
|
|
363
354
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
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
|
-
|
|
363
|
+
path.normalize(targetPath);
|
|
370
364
|
return true;
|
|
371
365
|
} catch {
|
|
372
366
|
return false;
|
|
373
367
|
}
|
|
374
368
|
}
|
|
375
369
|
|
|
376
|
-
|
|
377
|
-
|
|
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(
|
|
379
|
+
seenAliases.add(alias);
|
|
380
380
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
381
|
+
aliasMap.set(alias, target);
|
|
382
|
+
hasAliases = true;
|
|
383
|
+
aliasesChanged = true;
|
|
384
384
|
}
|
|
385
385
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
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
|
|
396
|
-
if (!
|
|
397
|
-
const
|
|
398
|
-
if (
|
|
399
|
-
let
|
|
400
|
-
const
|
|
401
|
-
while (
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
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
|
-
|
|
409
|
+
pathsChanged = false;
|
|
407
410
|
}
|
|
408
411
|
|
|
409
|
-
function
|
|
410
|
-
if (!
|
|
411
|
-
for (const
|
|
412
|
-
if (!
|
|
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
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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
|
-
|
|
428
|
-
if (
|
|
429
|
-
|
|
430
|
-
|
|
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
|
-
|
|
433
|
-
|
|
439
|
+
resolutionCache.max = cacheMaxSize;
|
|
440
|
+
evictionBatchSize = Math.floor(cacheMaxSize * 0.1);
|
|
434
441
|
}
|
|
435
442
|
|
|
436
|
-
if (
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
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
|
-
|
|
442
|
-
|
|
443
|
-
|
|
448
|
+
currentStrategy = STRATEGY_RADIX;
|
|
449
|
+
buildRadixTree();
|
|
450
|
+
sortedAliases = null;
|
|
444
451
|
}
|
|
445
452
|
}
|
|
446
453
|
|
|
447
|
-
function
|
|
448
|
-
|
|
449
|
-
|
|
454
|
+
function buildRadixTree() {
|
|
455
|
+
radixTree = new RadixTree();
|
|
456
|
+
aliasMap.forEach((target, alias) => radixTree.insert(alias, target));
|
|
450
457
|
}
|
|
451
458
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
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
|
-
|
|
465
|
-
|
|
476
|
+
reset();
|
|
477
|
+
initialize({ base: basePath, debug: debugMode, hotReload: hotReloadEnabled });
|
|
466
478
|
});
|
|
467
479
|
}
|
|
468
480
|
|
|
469
|
-
|
|
470
|
-
const
|
|
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
|
-
|
|
473
|
-
|
|
474
|
-
|
|
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
|
-
|
|
481
|
-
|
|
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 "${
|
|
498
|
+
`pathlra-aliaser Invalid alias target for "${alias}". Must be string or function`
|
|
484
499
|
);
|
|
485
500
|
}
|
|
486
|
-
const
|
|
487
|
-
|
|
501
|
+
const resolvedPath = target.startsWith("/") ? target : path.join(basePath, target);
|
|
502
|
+
addAlias(alias, resolvedPath);
|
|
488
503
|
}
|
|
489
504
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
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
|
-
|
|
496
|
-
|
|
511
|
+
optimizeStrategy();
|
|
512
|
+
applyPathsToCache();
|
|
497
513
|
|
|
498
|
-
const
|
|
499
|
-
if (
|
|
514
|
+
const duration = performance.now() - startTime;
|
|
515
|
+
if (duration > 20) {
|
|
500
516
|
console.warn(
|
|
501
|
-
`pathlra-aliaser Init took ${
|
|
502
|
-
|
|
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:
|
|
509
|
-
paths:
|
|
510
|
-
duration:
|
|
511
|
-
minimalMode:
|
|
524
|
+
aliases: aliasMap.size,
|
|
525
|
+
paths: customPathsSet.size,
|
|
526
|
+
duration: duration,
|
|
527
|
+
minimalMode: minimalMode,
|
|
512
528
|
};
|
|
513
529
|
}
|
|
514
530
|
|
|
515
|
-
function
|
|
516
|
-
if (typeof
|
|
517
|
-
if (
|
|
518
|
-
const
|
|
519
|
-
for (const
|
|
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
|
-
|
|
522
|
-
return
|
|
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${
|
|
541
|
+
throw new Error(`Failed to locate package.json in\n${candidates.join("\n")}`);
|
|
526
542
|
}
|
|
527
543
|
|
|
528
|
-
function
|
|
544
|
+
function loadPackageJson(basePath) {
|
|
529
545
|
try {
|
|
530
|
-
const
|
|
531
|
-
return JSON.parse(
|
|
532
|
-
} catch (
|
|
533
|
-
throw new Error(`Failed to load package.json: ${
|
|
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
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
553
|
+
function reset() {
|
|
554
|
+
resolutionCache.clear();
|
|
555
|
+
customPathsSet.clear();
|
|
556
|
+
aliasMap.clear();
|
|
541
557
|
seenAliases.clear();
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
const
|
|
553
|
-
if (
|
|
554
|
-
let
|
|
555
|
-
const
|
|
556
|
-
while (
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
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
|
|
562
|
-
for (const
|
|
563
|
-
if (
|
|
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
|
|
568
|
-
if (!
|
|
569
|
-
|
|
583
|
+
function cleanModulePaths(moduleData) {
|
|
584
|
+
if (!moduleData.paths) return;
|
|
585
|
+
moduleData.paths = moduleData.paths.filter((pathItem) => !customPathsSet.has(pathItem));
|
|
570
586
|
}
|
|
571
587
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
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
|
-
|
|
596
|
+
reset,
|
|
580
597
|
_internal: {
|
|
581
598
|
getStats: () => ({
|
|
582
|
-
aliases:
|
|
583
|
-
paths:
|
|
584
|
-
cacheSize:
|
|
585
|
-
strategy:
|
|
586
|
-
minimalMode:
|
|
587
|
-
hotReload:
|
|
588
|
-
debug:
|
|
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: (
|
|
592
|
-
|
|
593
|
-
if (
|
|
608
|
+
forceStrategy: (strategy) => {
|
|
609
|
+
currentStrategy = strategy;
|
|
610
|
+
if (strategy === STRATEGY_RADIX) buildRadixTree();
|
|
594
611
|
},
|
|
595
|
-
clearCache: () =>
|
|
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
|
-
|
|
602
|
-
let
|
|
603
|
-
if (!
|
|
604
|
-
compilerOptions.paths[alias + "/*"] = [
|
|
605
|
-
compilerOptions.paths[alias] = [
|
|
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
|
+
});
|