@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.
- package/README.md +13 -0
- package/dist/bundler.d.ts +14 -0
- package/dist/bundler.d.ts.map +1 -0
- package/dist/bundler.js +691 -0
- package/dist/bundler.js.map +1 -0
- package/dist/constants.d.ts +12 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +29 -0
- package/dist/constants.js.map +1 -0
- package/dist/core.d.ts +11 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +547 -0
- package/dist/core.js.map +1 -0
- package/dist/factory/detectors.d.ts +13 -0
- package/dist/factory/detectors.d.ts.map +1 -0
- package/dist/factory/detectors.js +46 -0
- package/dist/factory/detectors.js.map +1 -0
- package/dist/factory/hooks.d.ts +29 -0
- package/dist/factory/hooks.d.ts.map +1 -0
- package/dist/factory/hooks.js +154 -0
- package/dist/factory/hooks.js.map +1 -0
- package/dist/factory/index.d.ts +23 -0
- package/dist/factory/index.d.ts.map +1 -0
- package/dist/factory/index.js +47 -0
- package/dist/factory/index.js.map +1 -0
- package/dist/factory/presets.d.ts +27 -0
- package/dist/factory/presets.d.ts.map +1 -0
- package/dist/factory/presets.js +186 -0
- package/dist/factory/presets.js.map +1 -0
- package/dist/factory.d.ts +183 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/factory.js +482 -0
- package/dist/factory.js.map +1 -0
- package/dist/helpers.d.ts +53 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +177 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/route/index.d.ts +7 -0
- package/dist/route/index.d.ts.map +1 -0
- package/dist/route/index.js +6 -0
- package/dist/route/index.js.map +1 -0
- package/dist/route/parser.d.ts +18 -0
- package/dist/route/parser.d.ts.map +1 -0
- package/dist/route/parser.js +187 -0
- package/dist/route/parser.js.map +1 -0
- package/dist/route/regex.d.ts +31 -0
- package/dist/route/regex.d.ts.map +1 -0
- package/dist/route/regex.js +145 -0
- package/dist/route/regex.js.map +1 -0
- package/dist/route/regex.test.d.ts +7 -0
- package/dist/route/regex.test.d.ts.map +1 -0
- package/dist/route/regex.test.js +666 -0
- package/dist/route/regex.test.js.map +1 -0
- package/dist/route/types.d.ts +58 -0
- package/dist/route/types.d.ts.map +1 -0
- package/dist/route/types.js +5 -0
- package/dist/route/types.js.map +1 -0
- package/dist/route-parser.d.ts +8 -0
- package/dist/route-parser.d.ts.map +1 -0
- package/dist/route-parser.js +8 -0
- package/dist/route-parser.js.map +1 -0
- package/dist/types.d.ts +142 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +41 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +264 -0
- package/dist/utils.js.map +1 -0
- package/dist/vite-config-parser.d.ts +62 -0
- package/dist/vite-config-parser.d.ts.map +1 -0
- package/dist/vite-config-parser.js +229 -0
- package/dist/vite-config-parser.js.map +1 -0
- package/package.json +52 -0
package/dist/bundler.js
ADDED
|
@@ -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
|