@edgeone/vite-core 0.0.1-beta.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.
Files changed (78) hide show
  1. package/README.md +13 -0
  2. package/dist/bundler.d.ts +14 -0
  3. package/dist/bundler.d.ts.map +1 -0
  4. package/dist/bundler.js +691 -0
  5. package/dist/bundler.js.map +1 -0
  6. package/dist/constants.d.ts +12 -0
  7. package/dist/constants.d.ts.map +1 -0
  8. package/dist/constants.js +29 -0
  9. package/dist/constants.js.map +1 -0
  10. package/dist/core.d.ts +11 -0
  11. package/dist/core.d.ts.map +1 -0
  12. package/dist/core.js +547 -0
  13. package/dist/core.js.map +1 -0
  14. package/dist/factory/detectors.d.ts +13 -0
  15. package/dist/factory/detectors.d.ts.map +1 -0
  16. package/dist/factory/detectors.js +46 -0
  17. package/dist/factory/detectors.js.map +1 -0
  18. package/dist/factory/hooks.d.ts +29 -0
  19. package/dist/factory/hooks.d.ts.map +1 -0
  20. package/dist/factory/hooks.js +154 -0
  21. package/dist/factory/hooks.js.map +1 -0
  22. package/dist/factory/index.d.ts +23 -0
  23. package/dist/factory/index.d.ts.map +1 -0
  24. package/dist/factory/index.js +47 -0
  25. package/dist/factory/index.js.map +1 -0
  26. package/dist/factory/presets.d.ts +27 -0
  27. package/dist/factory/presets.d.ts.map +1 -0
  28. package/dist/factory/presets.js +186 -0
  29. package/dist/factory/presets.js.map +1 -0
  30. package/dist/factory.d.ts +183 -0
  31. package/dist/factory.d.ts.map +1 -0
  32. package/dist/factory.js +482 -0
  33. package/dist/factory.js.map +1 -0
  34. package/dist/helpers.d.ts +53 -0
  35. package/dist/helpers.d.ts.map +1 -0
  36. package/dist/helpers.js +177 -0
  37. package/dist/helpers.js.map +1 -0
  38. package/dist/index.d.ts +16 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +12 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/route/index.d.ts +7 -0
  43. package/dist/route/index.d.ts.map +1 -0
  44. package/dist/route/index.js +6 -0
  45. package/dist/route/index.js.map +1 -0
  46. package/dist/route/parser.d.ts +18 -0
  47. package/dist/route/parser.d.ts.map +1 -0
  48. package/dist/route/parser.js +187 -0
  49. package/dist/route/parser.js.map +1 -0
  50. package/dist/route/regex.d.ts +31 -0
  51. package/dist/route/regex.d.ts.map +1 -0
  52. package/dist/route/regex.js +145 -0
  53. package/dist/route/regex.js.map +1 -0
  54. package/dist/route/regex.test.d.ts +7 -0
  55. package/dist/route/regex.test.d.ts.map +1 -0
  56. package/dist/route/regex.test.js +666 -0
  57. package/dist/route/regex.test.js.map +1 -0
  58. package/dist/route/types.d.ts +58 -0
  59. package/dist/route/types.d.ts.map +1 -0
  60. package/dist/route/types.js +5 -0
  61. package/dist/route/types.js.map +1 -0
  62. package/dist/route-parser.d.ts +8 -0
  63. package/dist/route-parser.d.ts.map +1 -0
  64. package/dist/route-parser.js +8 -0
  65. package/dist/route-parser.js.map +1 -0
  66. package/dist/types.d.ts +142 -0
  67. package/dist/types.d.ts.map +1 -0
  68. package/dist/types.js +5 -0
  69. package/dist/types.js.map +1 -0
  70. package/dist/utils.d.ts +41 -0
  71. package/dist/utils.d.ts.map +1 -0
  72. package/dist/utils.js +264 -0
  73. package/dist/utils.js.map +1 -0
  74. package/dist/vite-config-parser.d.ts +62 -0
  75. package/dist/vite-config-parser.d.ts.map +1 -0
  76. package/dist/vite-config-parser.js +229 -0
  77. package/dist/vite-config-parser.js.map +1 -0
  78. package/package.json +52 -0
@@ -0,0 +1,691 @@
1
+ import * as esbuild from "esbuild";
2
+ import { nodeFileTrace } from "@vercel/nft";
3
+ import path from "path";
4
+ import fs from "fs";
5
+ import { writeFile, deleteFile, ensureDirectory, generateServerWrapperCode, readFile } from "./utils.js";
6
+ // ============================================================================
7
+ // Constants
8
+ // ============================================================================
9
+ const DEFAULT_BANNER = `import { createRequire } from 'node:module';
10
+ const require = createRequire(import.meta.url);
11
+ const __filename = new URL('', import.meta.url).pathname;
12
+ const __dirname = new URL('.', import.meta.url).pathname;`;
13
+ const MAX_PARENT_TRAVERSAL_DEPTH = 10;
14
+ const FILE_EXTENSIONS = ['.js', '.mjs', '.cjs', '.json'];
15
+ const DEFAULT_RESOLVE_CONDITIONS = ['node', 'import', 'require', 'default'];
16
+ // Pre-compiled regex patterns
17
+ const NODE_MODULES_PACKAGE_PATTERN = /node_modules\/(@[^/]+\/[^/]+|[^/]+)(\/.*)?$/;
18
+ const RELATIVE_NODE_MODULES_PATTERN = /^(?:\.\.?\/)+.*node_modules\//;
19
+ const BARE_IMPORT_PATTERN = /^[^./]/;
20
+ const PNPM_ENTRY_PATTERN = /^(@[^+]+\+[^@]+|[^@]+)@/;
21
+ // ============================================================================
22
+ // File System Cache
23
+ // ============================================================================
24
+ class FileSystemCache {
25
+ constructor() {
26
+ this.existsCache = new Map();
27
+ this.statCache = new Map();
28
+ this.realpathCache = new Map();
29
+ }
30
+ exists(filePath) {
31
+ let result = this.existsCache.get(filePath);
32
+ if (result === undefined) {
33
+ result = fs.existsSync(filePath);
34
+ this.existsCache.set(filePath, result);
35
+ }
36
+ return result;
37
+ }
38
+ isFile(filePath) {
39
+ let stat = this.statCache.get(filePath);
40
+ if (stat === undefined) {
41
+ try {
42
+ stat = fs.statSync(filePath);
43
+ }
44
+ catch {
45
+ stat = null;
46
+ }
47
+ this.statCache.set(filePath, stat);
48
+ }
49
+ return stat?.isFile() ?? false;
50
+ }
51
+ isDirectory(filePath) {
52
+ let stat = this.statCache.get(filePath);
53
+ if (stat === undefined) {
54
+ try {
55
+ stat = fs.statSync(filePath);
56
+ }
57
+ catch {
58
+ stat = null;
59
+ }
60
+ this.statCache.set(filePath, stat);
61
+ }
62
+ return stat?.isDirectory() ?? false;
63
+ }
64
+ realpath(filePath) {
65
+ let result = this.realpathCache.get(filePath);
66
+ if (result === undefined) {
67
+ try {
68
+ result = fs.realpathSync(filePath);
69
+ }
70
+ catch {
71
+ result = filePath;
72
+ }
73
+ this.realpathCache.set(filePath, result);
74
+ }
75
+ return result;
76
+ }
77
+ readdir(dirPath) {
78
+ try {
79
+ return fs.readdirSync(dirPath);
80
+ }
81
+ catch {
82
+ return [];
83
+ }
84
+ }
85
+ }
86
+ // ============================================================================
87
+ // Package JSON Cache
88
+ // ============================================================================
89
+ class PackageJsonCache {
90
+ constructor() {
91
+ this.cache = new Map();
92
+ }
93
+ get(packageRoot) {
94
+ const pkgJsonPath = path.join(packageRoot, 'package.json');
95
+ let result = this.cache.get(pkgJsonPath);
96
+ if (result === undefined) {
97
+ try {
98
+ const content = fs.readFileSync(pkgJsonPath, 'utf-8');
99
+ result = JSON.parse(content);
100
+ }
101
+ catch {
102
+ result = null;
103
+ }
104
+ this.cache.set(pkgJsonPath, result);
105
+ }
106
+ return result;
107
+ }
108
+ }
109
+ // ============================================================================
110
+ // Pnpm Resolver
111
+ // ============================================================================
112
+ class PnpmResolver {
113
+ constructor(projectRoot, fsCache) {
114
+ this.pnpmIndex = null;
115
+ this.subpathCache = new Map();
116
+ this.projectRoot = projectRoot;
117
+ this.fsCache = fsCache;
118
+ this.pnpmDir = path.join(projectRoot, 'node_modules/.pnpm');
119
+ }
120
+ isPnpmProject() {
121
+ return this.fsCache.exists(this.pnpmDir);
122
+ }
123
+ buildIndex() {
124
+ if (this.pnpmIndex)
125
+ return this.pnpmIndex;
126
+ this.pnpmIndex = new Map();
127
+ if (!this.fsCache.exists(this.pnpmDir)) {
128
+ return this.pnpmIndex;
129
+ }
130
+ const entries = this.fsCache.readdir(this.pnpmDir);
131
+ for (const entry of entries) {
132
+ const match = entry.match(PNPM_ENTRY_PATTERN);
133
+ if (match) {
134
+ const normalizedName = match[1].replace('+', '/');
135
+ if (!this.pnpmIndex.has(normalizedName)) {
136
+ this.pnpmIndex.set(normalizedName, entry);
137
+ }
138
+ }
139
+ }
140
+ return this.pnpmIndex;
141
+ }
142
+ findPackage(packageName) {
143
+ const index = this.buildIndex();
144
+ const entry = index.get(packageName);
145
+ if (entry) {
146
+ const fullPath = path.join(this.pnpmDir, entry, 'node_modules', packageName);
147
+ if (this.fsCache.exists(fullPath)) {
148
+ return this.fsCache.realpath(fullPath);
149
+ }
150
+ }
151
+ return null;
152
+ }
153
+ findPackageWithSubpath(packagePath, searchDir) {
154
+ const cacheKey = `${searchDir}::${packagePath}`;
155
+ const cached = this.subpathCache.get(cacheKey);
156
+ if (cached !== undefined) {
157
+ return cached;
158
+ }
159
+ const { packageName, subPath } = parsePackagePath(packagePath);
160
+ const pnpmDir = path.join(searchDir, 'node_modules/.pnpm');
161
+ if (!this.fsCache.exists(pnpmDir)) {
162
+ this.subpathCache.set(cacheKey, null);
163
+ return null;
164
+ }
165
+ const entries = this.fsCache.readdir(pnpmDir);
166
+ const normalizedName = packageName.replace('/', '+');
167
+ for (const entry of entries) {
168
+ if (entry.startsWith(normalizedName + '@') || entry === normalizedName) {
169
+ const fullPath = path.join(pnpmDir, entry, 'node_modules', packageName, subPath);
170
+ if (this.fsCache.exists(fullPath)) {
171
+ this.subpathCache.set(cacheKey, fullPath);
172
+ return fullPath;
173
+ }
174
+ }
175
+ }
176
+ this.subpathCache.set(cacheKey, null);
177
+ return null;
178
+ }
179
+ getVirtualStoreNodeModules() {
180
+ const pnpmNodeModules = path.join(this.projectRoot, 'node_modules/.pnpm/node_modules');
181
+ return this.fsCache.exists(pnpmNodeModules) ? pnpmNodeModules : null;
182
+ }
183
+ }
184
+ // ============================================================================
185
+ // Package Resolution Utilities
186
+ // ============================================================================
187
+ function parsePackagePath(importPath) {
188
+ const parts = importPath.split('/');
189
+ if (importPath.startsWith('@')) {
190
+ return {
191
+ packageName: `${parts[0]}/${parts[1]}`,
192
+ subPath: parts.slice(2).join('/')
193
+ };
194
+ }
195
+ return {
196
+ packageName: parts[0],
197
+ subPath: parts.slice(1).join('/')
198
+ };
199
+ }
200
+ function getResolveConditions(configConditions) {
201
+ const conditions = configConditions?.length ? [...configConditions] : [...DEFAULT_RESOLVE_CONDITIONS];
202
+ const envCondition = process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'development'
203
+ ? process.env.NODE_ENV
204
+ : null;
205
+ if (envCondition && !conditions.includes(envCondition)) {
206
+ conditions.push(envCondition);
207
+ }
208
+ return Array.from(new Set(conditions));
209
+ }
210
+ function resolveExportCondition(exportEntry, conditions = DEFAULT_RESOLVE_CONDITIONS) {
211
+ if (Array.isArray(exportEntry)) {
212
+ for (const entry of exportEntry) {
213
+ const resolved = resolveExportCondition(entry, conditions);
214
+ if (resolved) {
215
+ return resolved;
216
+ }
217
+ }
218
+ return null;
219
+ }
220
+ const resolveTarget = (value, depth = 0) => {
221
+ if (typeof value === 'string') {
222
+ return value;
223
+ }
224
+ if (typeof value !== 'object' || value === null || depth > 2) {
225
+ return null;
226
+ }
227
+ const record = value;
228
+ const nestedKeys = conditions.includes('default') ? conditions : [...conditions, 'default'];
229
+ for (const key of nestedKeys) {
230
+ if (Object.prototype.hasOwnProperty.call(record, key)) {
231
+ const resolved = resolveTarget(record[key], depth + 1);
232
+ if (resolved) {
233
+ return resolved;
234
+ }
235
+ }
236
+ }
237
+ return null;
238
+ };
239
+ if (typeof exportEntry === 'string') {
240
+ return exportEntry;
241
+ }
242
+ if (typeof exportEntry === 'object' && exportEntry !== null) {
243
+ const entry = exportEntry;
244
+ for (const cond of conditions) {
245
+ if (Object.prototype.hasOwnProperty.call(entry, cond)) {
246
+ const resolved = resolveTarget(entry[cond]);
247
+ if (resolved) {
248
+ return resolved;
249
+ }
250
+ }
251
+ }
252
+ const fallback = resolveTarget(entry.default ?? entry.import ?? entry.node ?? entry.require);
253
+ if (fallback) {
254
+ return fallback;
255
+ }
256
+ }
257
+ return null;
258
+ }
259
+ class PackageResolver {
260
+ constructor(fsCache, pkgJsonCache, conditions) {
261
+ this.fsCache = fsCache;
262
+ this.pkgJsonCache = pkgJsonCache;
263
+ this.conditions = conditions;
264
+ }
265
+ resolveEntry(packageRoot) {
266
+ const pkgJson = this.pkgJsonCache.get(packageRoot);
267
+ if (!pkgJson)
268
+ return null;
269
+ // 1. exports field (modern packages)
270
+ if (pkgJson.exports) {
271
+ const exports = pkgJson.exports;
272
+ if (typeof exports === 'string') {
273
+ return path.join(packageRoot, exports);
274
+ }
275
+ if (typeof exports === 'object' && exports !== null) {
276
+ const exportsObject = exports;
277
+ if (exportsObject['.']) {
278
+ const resolved = resolveExportCondition(exportsObject['.'], this.conditions);
279
+ if (resolved) {
280
+ return path.join(packageRoot, resolved);
281
+ }
282
+ }
283
+ else {
284
+ const resolved = resolveExportCondition(exportsObject, this.conditions);
285
+ if (resolved) {
286
+ return path.join(packageRoot, resolved);
287
+ }
288
+ }
289
+ }
290
+ }
291
+ // 2. module field (ESM)
292
+ if (pkgJson.module) {
293
+ return path.join(packageRoot, pkgJson.module);
294
+ }
295
+ // 3. main field
296
+ if (pkgJson.main) {
297
+ return path.join(packageRoot, pkgJson.main);
298
+ }
299
+ // 4. Default index.js
300
+ const defaultIndex = path.join(packageRoot, 'index.js');
301
+ if (this.fsCache.exists(defaultIndex)) {
302
+ return defaultIndex;
303
+ }
304
+ return null;
305
+ }
306
+ resolveSubpath(packageRoot, subPath) {
307
+ if (!subPath) {
308
+ const entryPath = this.resolveEntry(packageRoot);
309
+ return entryPath && this.fsCache.isFile(entryPath) ? entryPath : null;
310
+ }
311
+ const fullPath = path.join(packageRoot, subPath);
312
+ // Direct file match
313
+ if (this.fsCache.isFile(fullPath)) {
314
+ return fullPath;
315
+ }
316
+ // Try with extensions
317
+ for (const ext of FILE_EXTENSIONS) {
318
+ const pathWithExt = fullPath + ext;
319
+ if (this.fsCache.isFile(pathWithExt)) {
320
+ return pathWithExt;
321
+ }
322
+ }
323
+ // Try index files
324
+ for (const ext of FILE_EXTENSIONS) {
325
+ const indexPath = path.join(fullPath, `index${ext}`);
326
+ if (this.fsCache.isFile(indexPath)) {
327
+ return indexPath;
328
+ }
329
+ }
330
+ // Check package.json exports for subpath
331
+ const pkgJson = this.pkgJsonCache.get(packageRoot);
332
+ if (pkgJson?.exports && typeof pkgJson.exports === 'object') {
333
+ const exports = pkgJson.exports;
334
+ const subPathKey = './' + subPath;
335
+ const exportEntry = exports[subPathKey];
336
+ if (exportEntry) {
337
+ const resolved = resolveExportCondition(exportEntry, this.conditions);
338
+ if (resolved) {
339
+ const resolvedPath = path.join(packageRoot, resolved);
340
+ if (this.fsCache.isFile(resolvedPath)) {
341
+ return resolvedPath;
342
+ }
343
+ }
344
+ }
345
+ }
346
+ return null;
347
+ }
348
+ }
349
+ // ============================================================================
350
+ // Project Detection
351
+ // ============================================================================
352
+ export function detectPnpmProject(projectRoot) {
353
+ const indicators = [
354
+ path.join(projectRoot, 'pnpm-lock.yaml'),
355
+ path.join(projectRoot, 'pnpm-workspace.yaml'),
356
+ path.join(projectRoot, 'node_modules/.pnpm'),
357
+ ];
358
+ return indicators.some(p => fs.existsSync(p));
359
+ }
360
+ // ============================================================================
361
+ // NFT Dependency Tracing
362
+ // ============================================================================
363
+ async function traceAndBuildPathMap(entryPoints, projectRoot, fsCache, logger, conditions) {
364
+ const resolvedPaths = new Map();
365
+ try {
366
+ logger.verbose("Starting NFT file trace...");
367
+ const { fileList } = await nodeFileTrace(entryPoints, {
368
+ base: projectRoot,
369
+ processCwd: projectRoot,
370
+ conditions,
371
+ });
372
+ logger.verbose(`NFT traced ${fileList.size} files`);
373
+ // Build package name to path mapping from NFT results
374
+ for (const file of fileList) {
375
+ if (file.includes('node_modules')) {
376
+ const realPath = path.join(projectRoot, file);
377
+ const match = file.match(/node_modules\/(@[^/]+\/[^/]+|[^/]+)/);
378
+ if (match && !resolvedPaths.has(match[1])) {
379
+ const packageRoot = realPath.substring(0, realPath.indexOf(match[1]) + match[1].length);
380
+ resolvedPaths.set(match[1], fsCache.realpath(packageRoot));
381
+ logger.verbose(`NFT mapped: ${match[1]} -> ${packageRoot}`);
382
+ }
383
+ }
384
+ }
385
+ // For pnpm projects, scan nested dependencies
386
+ const isPnpm = fsCache.exists(path.join(projectRoot, 'node_modules/.pnpm'));
387
+ if (isPnpm) {
388
+ logger.verbose("Scanning pnpm nested dependencies...");
389
+ const additionalPackages = new Map();
390
+ for (const [, pkgPath] of resolvedPaths) {
391
+ const pkgNodeModules = path.join(pkgPath, '..');
392
+ if (!fsCache.exists(pkgNodeModules))
393
+ continue;
394
+ const entries = fsCache.readdir(pkgNodeModules);
395
+ for (const entry of entries) {
396
+ if (entry === '.pnpm' || entry === '.bin')
397
+ continue;
398
+ if (entry.startsWith('@')) {
399
+ const scopePath = path.join(pkgNodeModules, entry);
400
+ if (fsCache.isDirectory(scopePath)) {
401
+ const scopeEntries = fsCache.readdir(scopePath);
402
+ for (const scopeEntry of scopeEntries) {
403
+ const fullPkgName = `${entry}/${scopeEntry}`;
404
+ if (!resolvedPaths.has(fullPkgName) && !additionalPackages.has(fullPkgName)) {
405
+ additionalPackages.set(fullPkgName, fsCache.realpath(path.join(scopePath, scopeEntry)));
406
+ }
407
+ }
408
+ }
409
+ }
410
+ else {
411
+ const entryPath = path.join(pkgNodeModules, entry);
412
+ if (!resolvedPaths.has(entry) && !additionalPackages.has(entry) && fsCache.isDirectory(entryPath)) {
413
+ additionalPackages.set(entry, fsCache.realpath(entryPath));
414
+ }
415
+ }
416
+ }
417
+ }
418
+ for (const [pkgName, pkgPath] of additionalPackages) {
419
+ resolvedPaths.set(pkgName, pkgPath);
420
+ logger.verbose(`NFT additional: ${pkgName} -> ${pkgPath}`);
421
+ }
422
+ }
423
+ logger.verbose(`NFT resolved ${resolvedPaths.size} unique packages`);
424
+ }
425
+ catch (error) {
426
+ logger.verbose(`NFT trace failed: ${error}`);
427
+ }
428
+ return resolvedPaths;
429
+ }
430
+ // ============================================================================
431
+ // NFT Resolver Plugin
432
+ // ============================================================================
433
+ function createNftResolverPlugin(resolvedPaths, projectRoot, fsCache, pkgJsonCache, pnpmResolver, resolveConditions) {
434
+ const packageResolver = new PackageResolver(fsCache, pkgJsonCache, resolveConditions);
435
+ const isPnpm = pnpmResolver.isPnpmProject();
436
+ const findPackageRoot = (packageName, resolveDir) => {
437
+ // Check cache first
438
+ const cached = resolvedPaths.get(packageName);
439
+ if (cached)
440
+ return cached;
441
+ // Strategy 1: Project root node_modules
442
+ const rootPath = path.join(projectRoot, 'node_modules', packageName);
443
+ if (fsCache.exists(rootPath)) {
444
+ const resolved = fsCache.realpath(rootPath);
445
+ resolvedPaths.set(packageName, resolved);
446
+ return resolved;
447
+ }
448
+ // Strategy 2: Pnpm store
449
+ if (isPnpm) {
450
+ const pnpmPath = pnpmResolver.findPackage(packageName);
451
+ if (pnpmPath) {
452
+ resolvedPaths.set(packageName, pnpmPath);
453
+ return pnpmPath;
454
+ }
455
+ }
456
+ // Strategy 3: Sibling packages in pnpm
457
+ if (isPnpm) {
458
+ for (const [, knownPath] of resolvedPaths) {
459
+ const siblingPath = path.join(path.dirname(knownPath), packageName);
460
+ if (fsCache.exists(siblingPath)) {
461
+ const resolved = fsCache.realpath(siblingPath);
462
+ resolvedPaths.set(packageName, resolved);
463
+ return resolved;
464
+ }
465
+ }
466
+ }
467
+ // Strategy 4: Walk up from resolveDir (within project root)
468
+ if (resolveDir) {
469
+ let current = resolveDir;
470
+ while (current.startsWith(projectRoot) && current !== projectRoot) {
471
+ const directPath = path.join(current, 'node_modules', packageName);
472
+ if (fsCache.exists(directPath)) {
473
+ const resolved = fsCache.realpath(directPath);
474
+ resolvedPaths.set(packageName, resolved);
475
+ return resolved;
476
+ }
477
+ current = path.dirname(current);
478
+ }
479
+ }
480
+ return null;
481
+ };
482
+ return {
483
+ name: 'nft-resolver',
484
+ setup(build) {
485
+ // Handle relative paths containing node_modules (e.g., from Vite build output)
486
+ build.onResolve({ filter: RELATIVE_NODE_MODULES_PATTERN }, (args) => {
487
+ if (typeof args.path !== 'string' || args.path.startsWith('\0')) {
488
+ return null;
489
+ }
490
+ // Priority 1: Check if Vite build result exists
491
+ if (args.resolveDir) {
492
+ const absolutePath = path.resolve(args.resolveDir, args.path);
493
+ if (fsCache.isFile(absolutePath)) {
494
+ return { path: absolutePath };
495
+ }
496
+ }
497
+ // Priority 2: Redirect to project node_modules (monorepo fix)
498
+ const match = args.path.match(NODE_MODULES_PACKAGE_PATTERN);
499
+ if (!match)
500
+ return null;
501
+ const packageName = match[1];
502
+ const subPath = match[2] ? match[2].slice(1) : '';
503
+ const packageRoot = findPackageRoot(packageName, args.resolveDir);
504
+ if (packageRoot) {
505
+ const resolved = packageResolver.resolveSubpath(packageRoot, subPath);
506
+ if (resolved) {
507
+ return { path: resolved };
508
+ }
509
+ }
510
+ return null;
511
+ });
512
+ // Handle bare imports (e.g., 'lodash', '@tanstack/router')
513
+ build.onResolve({ filter: BARE_IMPORT_PATTERN }, (args) => {
514
+ if (typeof args.path !== 'string' || args.path.startsWith('\0')) {
515
+ return null;
516
+ }
517
+ if (args.path.startsWith('node:')) {
518
+ return { path: args.path, external: true };
519
+ }
520
+ const { packageName, subPath } = parsePackagePath(args.path);
521
+ const packageRoot = findPackageRoot(packageName, args.resolveDir);
522
+ if (packageRoot) {
523
+ const resolved = packageResolver.resolveSubpath(packageRoot, subPath);
524
+ if (resolved) {
525
+ return { path: resolved };
526
+ }
527
+ }
528
+ return null;
529
+ });
530
+ }
531
+ };
532
+ }
533
+ // ============================================================================
534
+ // ESM Interop Plugin
535
+ // ============================================================================
536
+ export function createEsmInteropPlugin(options) {
537
+ const { mappings, searchDirs = [] } = options;
538
+ return {
539
+ name: "esm-interop",
540
+ setup(build) {
541
+ const absWorkingDir = build.initialOptions.absWorkingDir || process.cwd();
542
+ const fsCache = new FileSystemCache();
543
+ const pnpmResolver = new PnpmResolver(absWorkingDir, fsCache);
544
+ const isPnpm = pnpmResolver.isPnpmProject();
545
+ const buildSearchDirs = (baseDir) => {
546
+ const dirs = new Set(searchDirs);
547
+ dirs.add(baseDir);
548
+ dirs.add(absWorkingDir);
549
+ dirs.add(process.cwd());
550
+ if (isPnpm) {
551
+ const pnpmNodeModules = pnpmResolver.getVirtualStoreNodeModules();
552
+ if (pnpmNodeModules) {
553
+ dirs.add(path.dirname(pnpmNodeModules));
554
+ }
555
+ }
556
+ // Walk up parent directories (monorepo support)
557
+ let current = baseDir;
558
+ const root = path.parse(current).root;
559
+ for (let i = 0; i < MAX_PARENT_TRAVERSAL_DEPTH && current !== root; i++) {
560
+ dirs.add(current);
561
+ const nodeModules = path.join(current, 'node_modules');
562
+ if (fsCache.exists(nodeModules)) {
563
+ dirs.add(current);
564
+ if (isPnpm) {
565
+ const pnpmDir = path.join(nodeModules, '.pnpm/node_modules');
566
+ if (fsCache.exists(pnpmDir)) {
567
+ dirs.add(path.dirname(pnpmDir));
568
+ }
569
+ }
570
+ }
571
+ current = path.dirname(current);
572
+ }
573
+ return Array.from(dirs);
574
+ };
575
+ // Create resolver for each mapping
576
+ for (const [from, to] of Object.entries(mappings)) {
577
+ const filter = new RegExp(`^${from.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`);
578
+ build.onResolve({ filter }, (args) => {
579
+ const searchDirList = buildSearchDirs(args.resolveDir || process.cwd());
580
+ for (const baseDir of searchDirList) {
581
+ // Method 1: Direct node_modules lookup
582
+ const directPath = path.join(baseDir, "node_modules", to);
583
+ if (fsCache.exists(directPath)) {
584
+ return { path: fsCache.realpath(directPath), external: false };
585
+ }
586
+ // Method 2: Pnpm virtual store lookup
587
+ if (isPnpm) {
588
+ const pnpmPath = pnpmResolver.findPackageWithSubpath(to, baseDir);
589
+ if (pnpmPath) {
590
+ return { path: pnpmPath, external: false };
591
+ }
592
+ }
593
+ }
594
+ return null;
595
+ });
596
+ }
597
+ },
598
+ };
599
+ }
600
+ // ============================================================================
601
+ // Main Bundle Function
602
+ // ============================================================================
603
+ export async function bundleServerCode(context, config) {
604
+ const { projectRoot, logger } = context;
605
+ logger.verbose("Bundling server code with NFT-enhanced resolution...");
606
+ await ensureDirectory(path.dirname(config.outfile));
607
+ // Initialize caches
608
+ const fsCache = new FileSystemCache();
609
+ const pkgJsonCache = new PackageJsonCache();
610
+ const pnpmResolver = new PnpmResolver(projectRoot, fsCache);
611
+ const isPnpm = pnpmResolver.isPnpmProject();
612
+ logger.verbose(`Package manager: ${isPnpm ? 'pnpm' : 'npm/yarn'}`);
613
+ const userConditions = Array.isArray(config.esbuildOptions?.conditions)
614
+ && config.esbuildOptions.conditions.every((condition) => typeof condition === 'string')
615
+ ? config.esbuildOptions.conditions
616
+ : undefined;
617
+ const resolveConditions = getResolveConditions(userConditions);
618
+ // Trace dependencies with NFT
619
+ const resolvedPaths = await traceAndBuildPathMap(config.entryPoints, projectRoot, fsCache, logger, resolveConditions);
620
+ // Build plugin list
621
+ const { plugins: customPlugins, ...restOptions } = config.esbuildOptions || {};
622
+ const plugins = [];
623
+ // 1. User plugins (higher priority)
624
+ if (Array.isArray(customPlugins)) {
625
+ plugins.push(...customPlugins);
626
+ }
627
+ // 2. NFT resolver plugin
628
+ plugins.push(createNftResolverPlugin(resolvedPaths, projectRoot, fsCache, pkgJsonCache, pnpmResolver, resolveConditions));
629
+ // Build nodePaths for project-local resolution
630
+ const nodePaths = [path.join(projectRoot, 'node_modules')];
631
+ if (isPnpm) {
632
+ const pnpmNodeModules = pnpmResolver.getVirtualStoreNodeModules();
633
+ if (pnpmNodeModules) {
634
+ nodePaths.push(pnpmNodeModules);
635
+ }
636
+ }
637
+ const result = await esbuild.build({
638
+ entryPoints: config.entryPoints,
639
+ bundle: true,
640
+ platform: "node",
641
+ target: "node18",
642
+ format: "esm",
643
+ outfile: config.outfile,
644
+ minify: false,
645
+ treeShaking: true,
646
+ external: ["node:*", ...(config.external || [])],
647
+ metafile: true,
648
+ logLevel: "warning",
649
+ banner: { js: DEFAULT_BANNER },
650
+ preserveSymlinks: false,
651
+ nodePaths,
652
+ plugins,
653
+ conditions: resolveConditions,
654
+ ...restOptions,
655
+ absWorkingDir: projectRoot,
656
+ });
657
+ logger.verbose(`Server code bundled to: ${config.outfile}`);
658
+ return result;
659
+ }
660
+ // ============================================================================
661
+ // Server Wrapper Utilities
662
+ // ============================================================================
663
+ export async function createServerWrapper(context, config) {
664
+ const { projectRoot, logger } = context;
665
+ logger.verbose("Creating server wrapper...");
666
+ let wrapperContent;
667
+ if (config.wrapperTemplate) {
668
+ const serverBuildContent = await readFile(config.serverEntryPath);
669
+ wrapperContent = config.wrapperTemplate.replace("{{SERVER_BUILD_CONTENT}}", serverBuildContent);
670
+ }
671
+ else {
672
+ wrapperContent = generateServerWrapperCode({
673
+ serverEntryPath: config.serverEntryPath,
674
+ handlerSetup: config.banner || "",
675
+ handlerCall: "defaultExport(webRequest, ...args)",
676
+ });
677
+ }
678
+ const tempPath = path.join(projectRoot, "server-wrapper.temp.js");
679
+ await writeFile(tempPath, wrapperContent);
680
+ logger.verbose("Server wrapper created");
681
+ return tempPath;
682
+ }
683
+ export async function cleanupWrapper(wrapperPath, logger) {
684
+ try {
685
+ await deleteFile(wrapperPath);
686
+ }
687
+ catch (error) {
688
+ logger?.verbose?.(`Failed to cleanup wrapper: ${error instanceof Error ? error.message : String(error)}`);
689
+ }
690
+ }
691
+ //# sourceMappingURL=bundler.js.map