elit 3.0.2 → 3.0.3
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/dist/cli.js +247 -31
- package/dist/server.d.ts +4 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +711 -148
- package/dist/server.mjs +710 -148
- package/package.json +1 -1
- package/src/server.ts +336 -33
package/package.json
CHANGED
package/src/server.ts
CHANGED
|
@@ -129,7 +129,7 @@ const send403 = (res: ServerResponse, msg = 'Forbidden'): void => sendError(res,
|
|
|
129
129
|
const send500 = (res: ServerResponse, msg = 'Internal Server Error'): void => sendError(res, 500, msg);
|
|
130
130
|
|
|
131
131
|
// Import map for all Elit client-side modules (reused in serveFile and serveSSR)
|
|
132
|
-
const createElitImportMap = (basePath: string = '', mode: 'dev' | 'preview' = 'dev'): string => {
|
|
132
|
+
const createElitImportMap = async (rootDir: string, basePath: string = '', mode: 'dev' | 'preview' = 'dev'): Promise<string> => {
|
|
133
133
|
// In dev mode, use built files from node_modules/elit/dist
|
|
134
134
|
// In preview mode, use built files from dist
|
|
135
135
|
const srcPath = mode === 'dev'
|
|
@@ -138,19 +138,26 @@ const createElitImportMap = (basePath: string = '', mode: 'dev' | 'preview' = 'd
|
|
|
138
138
|
|
|
139
139
|
const fileExt = mode === 'dev' ? '.ts' : '.mjs';
|
|
140
140
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
"elit":
|
|
144
|
-
"elit/":
|
|
145
|
-
"elit/dom":
|
|
146
|
-
"elit/state":
|
|
147
|
-
"elit/style":
|
|
148
|
-
"elit/el":
|
|
149
|
-
"elit/router":
|
|
150
|
-
"elit/hmr":
|
|
151
|
-
"elit/types":
|
|
152
|
-
}
|
|
153
|
-
|
|
141
|
+
// Base Elit imports
|
|
142
|
+
const elitImports: ImportMapEntry = {
|
|
143
|
+
"elit": `${srcPath}/index${fileExt}`,
|
|
144
|
+
"elit/": `${srcPath}/`,
|
|
145
|
+
"elit/dom": `${srcPath}/dom${fileExt}`,
|
|
146
|
+
"elit/state": `${srcPath}/state${fileExt}`,
|
|
147
|
+
"elit/style": `${srcPath}/style${fileExt}`,
|
|
148
|
+
"elit/el": `${srcPath}/el${fileExt}`,
|
|
149
|
+
"elit/router": `${srcPath}/router${fileExt}`,
|
|
150
|
+
"elit/hmr": `${srcPath}/hmr${fileExt}`,
|
|
151
|
+
"elit/types": `${srcPath}/types${fileExt}`
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// Generate external library imports
|
|
155
|
+
const externalImports = await generateExternalImportMaps(rootDir, basePath);
|
|
156
|
+
|
|
157
|
+
// Merge imports (Elit imports take precedence)
|
|
158
|
+
const allImports = { ...externalImports, ...elitImports };
|
|
159
|
+
|
|
160
|
+
return `<script type="importmap">${JSON.stringify({ imports: allImports }, null, 2)}</script>`;
|
|
154
161
|
};
|
|
155
162
|
|
|
156
163
|
// Helper function to generate HMR script (reused in serveFile and serveSSR)
|
|
@@ -193,6 +200,299 @@ async function findSpecialDir(startDir: string, targetDir: string): Promise<stri
|
|
|
193
200
|
return null;
|
|
194
201
|
}
|
|
195
202
|
|
|
203
|
+
// ===== External Library Import Maps =====
|
|
204
|
+
|
|
205
|
+
interface PackageExports {
|
|
206
|
+
[key: string]: string | PackageExports;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
interface PackageJson {
|
|
210
|
+
name?: string;
|
|
211
|
+
main?: string;
|
|
212
|
+
module?: string;
|
|
213
|
+
browser?: string | Record<string, string | false>;
|
|
214
|
+
exports?: string | PackageExports | { [key: string]: any };
|
|
215
|
+
type?: 'module' | 'commonjs';
|
|
216
|
+
sideEffects?: boolean | string[];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
interface ImportMapEntry {
|
|
220
|
+
[importName: string]: string;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Cache for generated import maps to avoid re-scanning
|
|
224
|
+
const importMapCache = new Map<string, ImportMapEntry>();
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Clear import map cache (useful when packages are added/removed)
|
|
228
|
+
*/
|
|
229
|
+
export function clearImportMapCache(): void {
|
|
230
|
+
importMapCache.clear();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Scan node_modules and generate import maps for external libraries
|
|
235
|
+
*/
|
|
236
|
+
async function generateExternalImportMaps(rootDir: string, basePath: string = ''): Promise<ImportMapEntry> {
|
|
237
|
+
const cacheKey = `${rootDir}:${basePath}`;
|
|
238
|
+
if (importMapCache.has(cacheKey)) {
|
|
239
|
+
return importMapCache.get(cacheKey)!;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const importMap: ImportMapEntry = {};
|
|
243
|
+
const nodeModulesPath = await findNodeModules(rootDir);
|
|
244
|
+
|
|
245
|
+
if (!nodeModulesPath) {
|
|
246
|
+
importMapCache.set(cacheKey, importMap);
|
|
247
|
+
return importMap;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
try {
|
|
251
|
+
const { readdir } = await import('./fs');
|
|
252
|
+
const packages = await readdir(nodeModulesPath);
|
|
253
|
+
|
|
254
|
+
for (const pkgEntry of packages) {
|
|
255
|
+
// Convert Dirent to string
|
|
256
|
+
const pkg = typeof pkgEntry === 'string' ? pkgEntry : pkgEntry.name;
|
|
257
|
+
|
|
258
|
+
// Skip special directories
|
|
259
|
+
if (pkg.startsWith('.')) continue;
|
|
260
|
+
|
|
261
|
+
// Handle scoped packages (@org/package)
|
|
262
|
+
if (pkg.startsWith('@')) {
|
|
263
|
+
try {
|
|
264
|
+
const scopedPackages = await readdir(join(nodeModulesPath, pkg));
|
|
265
|
+
for (const scopedEntry of scopedPackages) {
|
|
266
|
+
const scopedPkg = typeof scopedEntry === 'string' ? scopedEntry : scopedEntry.name;
|
|
267
|
+
const fullPkgName = `${pkg}/${scopedPkg}`;
|
|
268
|
+
await processPackage(nodeModulesPath, fullPkgName, importMap, basePath);
|
|
269
|
+
}
|
|
270
|
+
} catch {
|
|
271
|
+
// Skip if can't read scoped directory
|
|
272
|
+
}
|
|
273
|
+
} else {
|
|
274
|
+
await processPackage(nodeModulesPath, pkg, importMap, basePath);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
} catch (error) {
|
|
278
|
+
console.error('[Import Maps] Error scanning node_modules:', error);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
importMapCache.set(cacheKey, importMap);
|
|
282
|
+
return importMap;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Find node_modules directory by walking up the directory tree
|
|
287
|
+
*/
|
|
288
|
+
async function findNodeModules(startDir: string): Promise<string | null> {
|
|
289
|
+
const foundDir = await findSpecialDir(startDir, 'node_modules');
|
|
290
|
+
return foundDir ? join(foundDir, 'node_modules') : null;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Check if a package is browser-compatible
|
|
295
|
+
*/
|
|
296
|
+
function isBrowserCompatible(pkgName: string, pkgJson: PackageJson): boolean {
|
|
297
|
+
// Skip build tools, compilers, and Node.js-only packages
|
|
298
|
+
const buildTools = [
|
|
299
|
+
'typescript', 'esbuild', '@esbuild/',
|
|
300
|
+
'tsx', 'tsup', 'rollup', 'vite', 'webpack', 'parcel',
|
|
301
|
+
'terser', 'uglify', 'babel', '@babel/',
|
|
302
|
+
'postcss', 'autoprefixer', 'cssnano',
|
|
303
|
+
'sass', 'less', 'stylus'
|
|
304
|
+
];
|
|
305
|
+
|
|
306
|
+
const nodeOnly = [
|
|
307
|
+
'node-', '@node-', 'fsevents', 'chokidar',
|
|
308
|
+
'express', 'koa', 'fastify', 'nest',
|
|
309
|
+
'commander', 'yargs', 'inquirer', 'chalk', 'ora',
|
|
310
|
+
'nodemon', 'pm2', 'dotenv'
|
|
311
|
+
];
|
|
312
|
+
|
|
313
|
+
const testingTools = [
|
|
314
|
+
'jest', 'vitest', 'mocha', 'chai', 'jasmine',
|
|
315
|
+
'@jest/', '@testing-library/', '@vitest/',
|
|
316
|
+
'playwright', 'puppeteer', 'cypress'
|
|
317
|
+
];
|
|
318
|
+
|
|
319
|
+
const linters = [
|
|
320
|
+
'eslint', '@eslint/', 'prettier', 'tslint',
|
|
321
|
+
'stylelint', 'commitlint'
|
|
322
|
+
];
|
|
323
|
+
|
|
324
|
+
const typeDefinitions = [
|
|
325
|
+
'@types/', '@typescript-eslint/'
|
|
326
|
+
];
|
|
327
|
+
|
|
328
|
+
const utilities = [
|
|
329
|
+
'get-tsconfig', 'resolve-pkg-maps', 'pkg-types',
|
|
330
|
+
'fast-glob', 'globby', 'micromatch',
|
|
331
|
+
'execa', 'cross-spawn', 'shelljs'
|
|
332
|
+
];
|
|
333
|
+
|
|
334
|
+
// Combine all skip lists
|
|
335
|
+
const skipPatterns = [
|
|
336
|
+
...buildTools,
|
|
337
|
+
...nodeOnly,
|
|
338
|
+
...testingTools,
|
|
339
|
+
...linters,
|
|
340
|
+
...typeDefinitions,
|
|
341
|
+
...utilities
|
|
342
|
+
];
|
|
343
|
+
|
|
344
|
+
// Check if package name matches skip patterns
|
|
345
|
+
if (skipPatterns.some(pattern => pkgName.startsWith(pattern))) {
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Skip CommonJS-only lodash (prefer lodash-es)
|
|
350
|
+
if (pkgName === 'lodash') {
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Prefer packages with explicit browser field or module field (ESM)
|
|
355
|
+
if (pkgJson.browser || pkgJson.module) {
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Prefer packages with exports field that includes "import" or "browser"
|
|
360
|
+
if (pkgJson.exports) {
|
|
361
|
+
const exportsStr = JSON.stringify(pkgJson.exports);
|
|
362
|
+
if (exportsStr.includes('"import"') || exportsStr.includes('"browser"')) {
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Skip packages that are explicitly marked as type: "commonjs" without module/browser fields
|
|
368
|
+
if (pkgJson.type === 'commonjs' && !pkgJson.module && !pkgJson.browser) {
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Default: allow if it has exports or is type: "module"
|
|
373
|
+
return !!(pkgJson.exports || pkgJson.type === 'module' || pkgJson.module);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Process a single package and add its exports to the import map
|
|
378
|
+
*/
|
|
379
|
+
async function processPackage(
|
|
380
|
+
nodeModulesPath: string,
|
|
381
|
+
pkgName: string,
|
|
382
|
+
importMap: ImportMapEntry,
|
|
383
|
+
basePath: string
|
|
384
|
+
): Promise<void> {
|
|
385
|
+
const pkgPath = join(nodeModulesPath, pkgName);
|
|
386
|
+
const pkgJsonPath = join(pkgPath, 'package.json');
|
|
387
|
+
|
|
388
|
+
try {
|
|
389
|
+
const pkgJsonContent = await readFile(pkgJsonPath);
|
|
390
|
+
const pkgJson: PackageJson = JSON.parse(pkgJsonContent.toString());
|
|
391
|
+
|
|
392
|
+
// Check if package is browser-compatible
|
|
393
|
+
if (!isBrowserCompatible(pkgName, pkgJson)) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const baseUrl = basePath ? `${basePath}/node_modules/${pkgName}` : `/node_modules/${pkgName}`;
|
|
398
|
+
|
|
399
|
+
// Handle exports field (modern)
|
|
400
|
+
if (pkgJson.exports) {
|
|
401
|
+
processExportsField(pkgName, pkgJson.exports, baseUrl, importMap);
|
|
402
|
+
}
|
|
403
|
+
// Fallback to main/module/browser fields (legacy)
|
|
404
|
+
else {
|
|
405
|
+
const entryPoint = pkgJson.browser || pkgJson.module || pkgJson.main || 'index.js';
|
|
406
|
+
importMap[pkgName] = `${baseUrl}/${entryPoint}`;
|
|
407
|
+
|
|
408
|
+
// Add trailing slash for subpath imports
|
|
409
|
+
importMap[`${pkgName}/`] = `${baseUrl}/`;
|
|
410
|
+
}
|
|
411
|
+
} catch {
|
|
412
|
+
// Skip packages without package.json or invalid JSON
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Process package.json exports field and add to import map
|
|
418
|
+
*/
|
|
419
|
+
function processExportsField(
|
|
420
|
+
pkgName: string,
|
|
421
|
+
exports: string | PackageExports | { [key: string]: any },
|
|
422
|
+
baseUrl: string,
|
|
423
|
+
importMap: ImportMapEntry
|
|
424
|
+
): void {
|
|
425
|
+
// Simple string export
|
|
426
|
+
if (typeof exports === 'string') {
|
|
427
|
+
importMap[pkgName] = `${baseUrl}/${exports}`;
|
|
428
|
+
importMap[`${pkgName}/`] = `${baseUrl}/`;
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Object exports
|
|
433
|
+
if (typeof exports === 'object' && exports !== null) {
|
|
434
|
+
// Handle "." export (main entry)
|
|
435
|
+
if ('.' in exports) {
|
|
436
|
+
const dotExport = exports['.'];
|
|
437
|
+
const resolved = resolveExport(dotExport);
|
|
438
|
+
if (resolved) {
|
|
439
|
+
importMap[pkgName] = `${baseUrl}/${resolved}`;
|
|
440
|
+
}
|
|
441
|
+
} else if ('import' in exports) {
|
|
442
|
+
// Root-level import/require
|
|
443
|
+
const resolved = resolveExport(exports);
|
|
444
|
+
if (resolved) {
|
|
445
|
+
importMap[pkgName] = `${baseUrl}/${resolved}`;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Handle subpath exports
|
|
450
|
+
for (const [key, value] of Object.entries(exports)) {
|
|
451
|
+
if (key === '.' || key === 'import' || key === 'require' || key === 'types' || key === 'default') {
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const resolved = resolveExport(value);
|
|
456
|
+
if (resolved) {
|
|
457
|
+
// Remove leading ./ from key
|
|
458
|
+
const cleanKey = key.startsWith('./') ? key.slice(2) : key;
|
|
459
|
+
const importName = cleanKey ? `${pkgName}/${cleanKey}` : pkgName;
|
|
460
|
+
importMap[importName] = `${baseUrl}/${resolved}`;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Always add trailing slash for subpath imports
|
|
465
|
+
importMap[`${pkgName}/`] = `${baseUrl}/`;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Resolve export value to actual file path
|
|
471
|
+
* Handles conditional exports (import/require/default)
|
|
472
|
+
*/
|
|
473
|
+
function resolveExport(exportValue: any): string | null {
|
|
474
|
+
if (typeof exportValue === 'string') {
|
|
475
|
+
// Remove leading ./
|
|
476
|
+
return exportValue.startsWith('./') ? exportValue.slice(2) : exportValue;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (typeof exportValue === 'object' && exportValue !== null) {
|
|
480
|
+
// Prefer import over require over default
|
|
481
|
+
const resolved = exportValue.import || exportValue.browser || exportValue.default || exportValue.require;
|
|
482
|
+
|
|
483
|
+
// Handle nested objects recursively (e.g., TypeScript's complex exports)
|
|
484
|
+
if (typeof resolved === 'object' && resolved !== null) {
|
|
485
|
+
return resolveExport(resolved);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (typeof resolved === 'string') {
|
|
489
|
+
return resolved.startsWith('./') ? resolved.slice(2) : resolved;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
|
|
196
496
|
// ===== Middleware =====
|
|
197
497
|
|
|
198
498
|
export function cors(options: {
|
|
@@ -694,7 +994,7 @@ export function createDevServer(options: DevServerOptions): DevServer {
|
|
|
694
994
|
let filePath: string;
|
|
695
995
|
if (url === '/' && matchedClient.ssr && !matchedClient.index) {
|
|
696
996
|
// Use SSR directly when configured and no custom index specified
|
|
697
|
-
return serveSSR(res, matchedClient);
|
|
997
|
+
return await serveSSR(res, matchedClient);
|
|
698
998
|
} else {
|
|
699
999
|
// Use custom index file if specified, otherwise default to /index.html
|
|
700
1000
|
filePath = url === '/' ? (matchedClient.index || '/index.html') : url;
|
|
@@ -809,7 +1109,7 @@ export function createDevServer(options: DevServerOptions): DevServer {
|
|
|
809
1109
|
if (!res.headersSent) {
|
|
810
1110
|
// If index.html not found but SSR function exists, use SSR
|
|
811
1111
|
if (filePath === '/index.html' && matchedClient.ssr) {
|
|
812
|
-
return serveSSR(res, matchedClient);
|
|
1112
|
+
return await serveSSR(res, matchedClient);
|
|
813
1113
|
}
|
|
814
1114
|
if (config.logging) console.log(`[404] ${filePath}`);
|
|
815
1115
|
return send404(res, '404 Not Found');
|
|
@@ -840,7 +1140,7 @@ export function createDevServer(options: DevServerOptions): DevServer {
|
|
|
840
1140
|
if (config.logging) console.log(`[DEBUG] No index file found in directory`);
|
|
841
1141
|
// If index.html not found in directory but SSR function exists, use SSR
|
|
842
1142
|
if (matchedClient.ssr) {
|
|
843
|
-
return serveSSR(res, matchedClient);
|
|
1143
|
+
return await serveSSR(res, matchedClient);
|
|
844
1144
|
}
|
|
845
1145
|
return send404(res, '404 Not Found');
|
|
846
1146
|
}
|
|
@@ -866,13 +1166,13 @@ export function createDevServer(options: DevServerOptions): DevServer {
|
|
|
866
1166
|
return send403(res, '403 Forbidden');
|
|
867
1167
|
}
|
|
868
1168
|
await stat(indexPath);
|
|
869
|
-
return serveFile(indexPath, res, matchedClient);
|
|
1169
|
+
return serveFile(indexPath, res, matchedClient, isDistRequest || isNodeModulesRequest);
|
|
870
1170
|
} catch {
|
|
871
1171
|
return send404(res, '404 Not Found');
|
|
872
1172
|
}
|
|
873
1173
|
}
|
|
874
1174
|
|
|
875
|
-
await serveFile(fullPath, res, matchedClient);
|
|
1175
|
+
await serveFile(fullPath, res, matchedClient, isDistRequest || isNodeModulesRequest);
|
|
876
1176
|
} catch (error) {
|
|
877
1177
|
// Only send 404 if response hasn't been sent yet
|
|
878
1178
|
if (!res.headersSent) {
|
|
@@ -883,22 +1183,17 @@ export function createDevServer(options: DevServerOptions): DevServer {
|
|
|
883
1183
|
});
|
|
884
1184
|
|
|
885
1185
|
// Serve file helper
|
|
886
|
-
async function serveFile(filePath: string, res: ServerResponse, client: NormalizedClient) {
|
|
1186
|
+
async function serveFile(filePath: string, res: ServerResponse, client: NormalizedClient, isNodeModulesOrDist: boolean = false) {
|
|
887
1187
|
try {
|
|
888
1188
|
const rootDir = await realpath(resolve(client.root));
|
|
889
1189
|
|
|
890
1190
|
// Security: Check path before resolving symlinks
|
|
891
1191
|
const unresolvedPath = resolve(filePath);
|
|
892
|
-
const isNodeModules = filePath.includes('/node_modules/') || filePath.includes('\\node_modules\\');
|
|
893
|
-
const isDist = filePath.includes('/dist/') || filePath.includes('\\dist\\');
|
|
894
|
-
|
|
895
|
-
// Check if path is within project root (for symlinked packages like node_modules/elit)
|
|
896
|
-
const projectRoot = await realpath(resolve(client.root, '..'));
|
|
897
|
-
const isInProjectRoot = unresolvedPath.startsWith(projectRoot + sep) || unresolvedPath === projectRoot;
|
|
898
1192
|
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
if
|
|
1193
|
+
// Skip security check for node_modules and dist (these may be symlinks)
|
|
1194
|
+
if (!isNodeModulesOrDist) {
|
|
1195
|
+
// Check if path is within project root
|
|
1196
|
+
if (!unresolvedPath.startsWith(rootDir + sep) && unresolvedPath !== rootDir) {
|
|
902
1197
|
if (config.logging) console.log(`[403] Attempted to serve file outside allowed directories: ${filePath}`);
|
|
903
1198
|
return send403(res, '403 Forbidden');
|
|
904
1199
|
}
|
|
@@ -908,10 +1203,18 @@ export function createDevServer(options: DevServerOptions): DevServer {
|
|
|
908
1203
|
let resolvedPath;
|
|
909
1204
|
try {
|
|
910
1205
|
resolvedPath = await realpath(unresolvedPath);
|
|
1206
|
+
|
|
1207
|
+
// For symlinked packages (like node_modules/elit), allow serving from outside rootDir
|
|
1208
|
+
if (isNodeModulesOrDist && resolvedPath) {
|
|
1209
|
+
// Allow it - this is a symlinked package
|
|
1210
|
+
if (config.logging && !resolvedPath.startsWith(rootDir + sep)) {
|
|
1211
|
+
console.log(`[DEBUG] Serving symlinked file: ${resolvedPath}`);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
911
1214
|
} catch {
|
|
912
1215
|
// If index.html not found but SSR function exists, use SSR
|
|
913
1216
|
if (filePath.endsWith('index.html') && client.ssr) {
|
|
914
|
-
return serveSSR(res, client);
|
|
1217
|
+
return await serveSSR(res, client);
|
|
915
1218
|
}
|
|
916
1219
|
return send404(res, '404 Not Found');
|
|
917
1220
|
}
|
|
@@ -1048,7 +1351,7 @@ export function createDevServer(options: DevServerOptions): DevServer {
|
|
|
1048
1351
|
}
|
|
1049
1352
|
|
|
1050
1353
|
// Inject import map and SSR styles into <head>
|
|
1051
|
-
const elitImportMap = createElitImportMap(basePath, client.mode);
|
|
1354
|
+
const elitImportMap = await createElitImportMap(client.root, basePath, client.mode);
|
|
1052
1355
|
const headInjection = ssrStyles ? `${ssrStyles}\n${elitImportMap}` : elitImportMap;
|
|
1053
1356
|
html = html.includes('</head>') ? html.replace('</head>', `${headInjection}</head>`) : html;
|
|
1054
1357
|
html = html.includes('</body>') ? html.replace('</body>', `${hmrScript}</body>`) : html + hmrScript;
|
|
@@ -1088,7 +1391,7 @@ export function createDevServer(options: DevServerOptions): DevServer {
|
|
|
1088
1391
|
}
|
|
1089
1392
|
|
|
1090
1393
|
// SSR helper - Generate HTML from SSR function
|
|
1091
|
-
function serveSSR(res: ServerResponse, client: NormalizedClient) {
|
|
1394
|
+
async function serveSSR(res: ServerResponse, client: NormalizedClient) {
|
|
1092
1395
|
try {
|
|
1093
1396
|
if (!client.ssr) {
|
|
1094
1397
|
return send500(res, 'SSR function not configured');
|
|
@@ -1122,7 +1425,7 @@ export function createDevServer(options: DevServerOptions): DevServer {
|
|
|
1122
1425
|
const hmrScript = createHMRScript(config.port, basePath);
|
|
1123
1426
|
|
|
1124
1427
|
// Inject import map in head, HMR script in body
|
|
1125
|
-
const elitImportMap = createElitImportMap(basePath, client.mode);
|
|
1428
|
+
const elitImportMap = await createElitImportMap(client.root, basePath, client.mode);
|
|
1126
1429
|
html = html.includes('</head>') ? html.replace('</head>', `${elitImportMap}</head>`) : html;
|
|
1127
1430
|
html = html.includes('</body>') ? html.replace('</body>', `${hmrScript}</body>`) : html + hmrScript;
|
|
1128
1431
|
|