elit 3.0.2 → 3.0.4
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 +254 -44
- package/dist/server.d.ts +4 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +713 -150
- package/dist/server.mjs +712 -150
- package/dist/types.d.ts +0 -6
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cli.ts +5 -13
- package/src/server.ts +339 -38
- package/src/types.ts +0 -6
package/dist/types.d.ts
CHANGED
|
@@ -78,8 +78,6 @@ export interface ClientConfig {
|
|
|
78
78
|
worker?: WorkerConfig[];
|
|
79
79
|
/** API router for REST endpoints specific to this client */
|
|
80
80
|
api?: Router;
|
|
81
|
-
/** Custom middleware specific to this client */
|
|
82
|
-
middleware?: ((req: any, res: any, next: () => void) => void)[];
|
|
83
81
|
/** Server mode: 'dev' uses source files, 'preview' uses built files (default: 'dev') */
|
|
84
82
|
mode?: 'dev' | 'preview';
|
|
85
83
|
}
|
|
@@ -130,8 +128,6 @@ export interface DevServerOptions {
|
|
|
130
128
|
worker?: WorkerConfig[];
|
|
131
129
|
/** Enable logging (default: true) */
|
|
132
130
|
logging?: boolean;
|
|
133
|
-
/** Custom middleware */
|
|
134
|
-
middleware?: ((req: any, res: any, next: () => void) => void)[];
|
|
135
131
|
/** API router for REST endpoints */
|
|
136
132
|
api?: Router;
|
|
137
133
|
/** SSR render function - returns HTML VNode or string */
|
|
@@ -224,8 +220,6 @@ export interface PreviewOptions {
|
|
|
224
220
|
open?: boolean;
|
|
225
221
|
/** Enable logging (default: true) */
|
|
226
222
|
logging?: boolean;
|
|
227
|
-
/** Custom middleware */
|
|
228
|
-
middleware?: ((req: any, res: any, next: () => void) => void)[];
|
|
229
223
|
/** API router for REST endpoints */
|
|
230
224
|
api?: Router;
|
|
231
225
|
/** SSR render function - returns HTML VNode or string */
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -247,11 +247,6 @@ async function runPreview(args: string[]) {
|
|
|
247
247
|
options.https = mergedOptions.https;
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
-
// Add middleware if configured
|
|
251
|
-
if (mergedOptions.middleware) {
|
|
252
|
-
options.middleware = mergedOptions.middleware;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
250
|
// Add SSR if configured
|
|
256
251
|
if (mergedOptions.ssr) {
|
|
257
252
|
options.ssr = mergedOptions.ssr;
|
|
@@ -403,23 +398,20 @@ Worker Configuration:
|
|
|
403
398
|
2. Global workers (defined in dev.worker or preview.worker)
|
|
404
399
|
Both global and client-specific workers will be loaded.
|
|
405
400
|
|
|
406
|
-
API
|
|
407
|
-
Configure REST API endpoints
|
|
401
|
+
API Configuration:
|
|
402
|
+
Configure REST API endpoints per client or globally.
|
|
408
403
|
Supports both global configuration and client-specific configuration.
|
|
409
404
|
|
|
410
|
-
Client-specific API
|
|
405
|
+
Client-specific API:
|
|
411
406
|
- Each client can have its own API router (clients[].api)
|
|
412
|
-
- Each client can have its own middleware chain (clients[].middleware)
|
|
413
407
|
- Client-specific configuration is isolated to that client's routes
|
|
414
408
|
- API paths are automatically prefixed with the client's basePath
|
|
415
409
|
Example: If basePath is '/app1' and route is '/api/health',
|
|
416
410
|
the full path will be '/app1/api/health'
|
|
417
411
|
|
|
418
412
|
Priority:
|
|
419
|
-
1. Client-specific
|
|
420
|
-
2. Global
|
|
421
|
-
3. Client-specific API routes are matched (defined in clients[].api)
|
|
422
|
-
4. Global API routes are matched (defined in dev.api or preview.api)
|
|
413
|
+
1. Client-specific API routes are matched first (defined in clients[].api)
|
|
414
|
+
2. Global API routes are matched second (defined in dev.api or preview.api)
|
|
423
415
|
|
|
424
416
|
Examples:
|
|
425
417
|
elit dev
|
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: {
|
|
@@ -390,14 +690,13 @@ export function createProxyHandler(proxyConfigs: ProxyConfig[]) {
|
|
|
390
690
|
}
|
|
391
691
|
}
|
|
392
692
|
|
|
393
|
-
// Change origin if requested
|
|
693
|
+
// Change origin if requested (or remove host header if not)
|
|
394
694
|
if (changeOrigin) {
|
|
395
695
|
proxyReqHeaders.host = targetUrl.host;
|
|
696
|
+
} else {
|
|
697
|
+
delete proxyReqHeaders['host'];
|
|
396
698
|
}
|
|
397
699
|
|
|
398
|
-
// Remove headers that shouldn't be forwarded
|
|
399
|
-
delete proxyReqHeaders['host'];
|
|
400
|
-
|
|
401
700
|
const proxyReqOptions = {
|
|
402
701
|
method: req.method,
|
|
403
702
|
headers: proxyReqHeaders
|
|
@@ -585,7 +884,6 @@ const defaultOptions: Omit<Required<DevServerOptions>, 'api' | 'clients' | 'root
|
|
|
585
884
|
watch: ['**/*.ts', '**/*.js', '**/*.html', '**/*.css'],
|
|
586
885
|
ignore: ['node_modules/**', 'dist/**', '.git/**', '**/*.d.ts'],
|
|
587
886
|
logging: true,
|
|
588
|
-
middleware: [],
|
|
589
887
|
worker: [],
|
|
590
888
|
mode: 'dev'
|
|
591
889
|
};
|
|
@@ -694,7 +992,7 @@ export function createDevServer(options: DevServerOptions): DevServer {
|
|
|
694
992
|
let filePath: string;
|
|
695
993
|
if (url === '/' && matchedClient.ssr && !matchedClient.index) {
|
|
696
994
|
// Use SSR directly when configured and no custom index specified
|
|
697
|
-
return serveSSR(res, matchedClient);
|
|
995
|
+
return await serveSSR(res, matchedClient);
|
|
698
996
|
} else {
|
|
699
997
|
// Use custom index file if specified, otherwise default to /index.html
|
|
700
998
|
filePath = url === '/' ? (matchedClient.index || '/index.html') : url;
|
|
@@ -809,7 +1107,7 @@ export function createDevServer(options: DevServerOptions): DevServer {
|
|
|
809
1107
|
if (!res.headersSent) {
|
|
810
1108
|
// If index.html not found but SSR function exists, use SSR
|
|
811
1109
|
if (filePath === '/index.html' && matchedClient.ssr) {
|
|
812
|
-
return serveSSR(res, matchedClient);
|
|
1110
|
+
return await serveSSR(res, matchedClient);
|
|
813
1111
|
}
|
|
814
1112
|
if (config.logging) console.log(`[404] ${filePath}`);
|
|
815
1113
|
return send404(res, '404 Not Found');
|
|
@@ -840,7 +1138,7 @@ export function createDevServer(options: DevServerOptions): DevServer {
|
|
|
840
1138
|
if (config.logging) console.log(`[DEBUG] No index file found in directory`);
|
|
841
1139
|
// If index.html not found in directory but SSR function exists, use SSR
|
|
842
1140
|
if (matchedClient.ssr) {
|
|
843
|
-
return serveSSR(res, matchedClient);
|
|
1141
|
+
return await serveSSR(res, matchedClient);
|
|
844
1142
|
}
|
|
845
1143
|
return send404(res, '404 Not Found');
|
|
846
1144
|
}
|
|
@@ -866,13 +1164,13 @@ export function createDevServer(options: DevServerOptions): DevServer {
|
|
|
866
1164
|
return send403(res, '403 Forbidden');
|
|
867
1165
|
}
|
|
868
1166
|
await stat(indexPath);
|
|
869
|
-
return serveFile(indexPath, res, matchedClient);
|
|
1167
|
+
return serveFile(indexPath, res, matchedClient, isDistRequest || isNodeModulesRequest);
|
|
870
1168
|
} catch {
|
|
871
1169
|
return send404(res, '404 Not Found');
|
|
872
1170
|
}
|
|
873
1171
|
}
|
|
874
1172
|
|
|
875
|
-
await serveFile(fullPath, res, matchedClient);
|
|
1173
|
+
await serveFile(fullPath, res, matchedClient, isDistRequest || isNodeModulesRequest);
|
|
876
1174
|
} catch (error) {
|
|
877
1175
|
// Only send 404 if response hasn't been sent yet
|
|
878
1176
|
if (!res.headersSent) {
|
|
@@ -883,22 +1181,17 @@ export function createDevServer(options: DevServerOptions): DevServer {
|
|
|
883
1181
|
});
|
|
884
1182
|
|
|
885
1183
|
// Serve file helper
|
|
886
|
-
async function serveFile(filePath: string, res: ServerResponse, client: NormalizedClient) {
|
|
1184
|
+
async function serveFile(filePath: string, res: ServerResponse, client: NormalizedClient, isNodeModulesOrDist: boolean = false) {
|
|
887
1185
|
try {
|
|
888
1186
|
const rootDir = await realpath(resolve(client.root));
|
|
889
1187
|
|
|
890
1188
|
// Security: Check path before resolving symlinks
|
|
891
1189
|
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
1190
|
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
if
|
|
1191
|
+
// Skip security check for node_modules and dist (these may be symlinks)
|
|
1192
|
+
if (!isNodeModulesOrDist) {
|
|
1193
|
+
// Check if path is within project root
|
|
1194
|
+
if (!unresolvedPath.startsWith(rootDir + sep) && unresolvedPath !== rootDir) {
|
|
902
1195
|
if (config.logging) console.log(`[403] Attempted to serve file outside allowed directories: ${filePath}`);
|
|
903
1196
|
return send403(res, '403 Forbidden');
|
|
904
1197
|
}
|
|
@@ -908,10 +1201,18 @@ export function createDevServer(options: DevServerOptions): DevServer {
|
|
|
908
1201
|
let resolvedPath;
|
|
909
1202
|
try {
|
|
910
1203
|
resolvedPath = await realpath(unresolvedPath);
|
|
1204
|
+
|
|
1205
|
+
// For symlinked packages (like node_modules/elit), allow serving from outside rootDir
|
|
1206
|
+
if (isNodeModulesOrDist && resolvedPath) {
|
|
1207
|
+
// Allow it - this is a symlinked package
|
|
1208
|
+
if (config.logging && !resolvedPath.startsWith(rootDir + sep)) {
|
|
1209
|
+
console.log(`[DEBUG] Serving symlinked file: ${resolvedPath}`);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
911
1212
|
} catch {
|
|
912
1213
|
// If index.html not found but SSR function exists, use SSR
|
|
913
1214
|
if (filePath.endsWith('index.html') && client.ssr) {
|
|
914
|
-
return serveSSR(res, client);
|
|
1215
|
+
return await serveSSR(res, client);
|
|
915
1216
|
}
|
|
916
1217
|
return send404(res, '404 Not Found');
|
|
917
1218
|
}
|
|
@@ -1048,7 +1349,7 @@ export function createDevServer(options: DevServerOptions): DevServer {
|
|
|
1048
1349
|
}
|
|
1049
1350
|
|
|
1050
1351
|
// Inject import map and SSR styles into <head>
|
|
1051
|
-
const elitImportMap = createElitImportMap(basePath, client.mode);
|
|
1352
|
+
const elitImportMap = await createElitImportMap(client.root, basePath, client.mode);
|
|
1052
1353
|
const headInjection = ssrStyles ? `${ssrStyles}\n${elitImportMap}` : elitImportMap;
|
|
1053
1354
|
html = html.includes('</head>') ? html.replace('</head>', `${headInjection}</head>`) : html;
|
|
1054
1355
|
html = html.includes('</body>') ? html.replace('</body>', `${hmrScript}</body>`) : html + hmrScript;
|
|
@@ -1088,7 +1389,7 @@ export function createDevServer(options: DevServerOptions): DevServer {
|
|
|
1088
1389
|
}
|
|
1089
1390
|
|
|
1090
1391
|
// SSR helper - Generate HTML from SSR function
|
|
1091
|
-
function serveSSR(res: ServerResponse, client: NormalizedClient) {
|
|
1392
|
+
async function serveSSR(res: ServerResponse, client: NormalizedClient) {
|
|
1092
1393
|
try {
|
|
1093
1394
|
if (!client.ssr) {
|
|
1094
1395
|
return send500(res, 'SSR function not configured');
|
|
@@ -1122,7 +1423,7 @@ export function createDevServer(options: DevServerOptions): DevServer {
|
|
|
1122
1423
|
const hmrScript = createHMRScript(config.port, basePath);
|
|
1123
1424
|
|
|
1124
1425
|
// Inject import map in head, HMR script in body
|
|
1125
|
-
const elitImportMap = createElitImportMap(basePath, client.mode);
|
|
1426
|
+
const elitImportMap = await createElitImportMap(client.root, basePath, client.mode);
|
|
1126
1427
|
html = html.includes('</head>') ? html.replace('</head>', `${elitImportMap}</head>`) : html;
|
|
1127
1428
|
html = html.includes('</body>') ? html.replace('</body>', `${hmrScript}</body>`) : html + hmrScript;
|
|
1128
1429
|
|
package/src/types.ts
CHANGED
|
@@ -96,8 +96,6 @@ export interface ClientConfig {
|
|
|
96
96
|
worker?: WorkerConfig[];
|
|
97
97
|
/** API router for REST endpoints specific to this client */
|
|
98
98
|
api?: Router;
|
|
99
|
-
/** Custom middleware specific to this client */
|
|
100
|
-
middleware?: ((req: any, res: any, next: () => void) => void)[];
|
|
101
99
|
/** Server mode: 'dev' uses source files, 'preview' uses built files (default: 'dev') */
|
|
102
100
|
mode?: 'dev' | 'preview';
|
|
103
101
|
}
|
|
@@ -151,8 +149,6 @@ export interface DevServerOptions {
|
|
|
151
149
|
worker?: WorkerConfig[];
|
|
152
150
|
/** Enable logging (default: true) */
|
|
153
151
|
logging?: boolean;
|
|
154
|
-
/** Custom middleware */
|
|
155
|
-
middleware?: ((req: any, res: any, next: () => void) => void)[];
|
|
156
152
|
/** API router for REST endpoints */
|
|
157
153
|
api?: Router;
|
|
158
154
|
/** SSR render function - returns HTML VNode or string */
|
|
@@ -250,8 +246,6 @@ export interface PreviewOptions {
|
|
|
250
246
|
open?: boolean;
|
|
251
247
|
/** Enable logging (default: true) */
|
|
252
248
|
logging?: boolean;
|
|
253
|
-
/** Custom middleware */
|
|
254
|
-
middleware?: ((req: any, res: any, next: () => void) => void)[];
|
|
255
249
|
/** API router for REST endpoints */
|
|
256
250
|
api?: Router;
|
|
257
251
|
/** SSR render function - returns HTML VNode or string */
|