@emberkit/core 0.3.8 → 0.4.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/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/vite-plugin/index.js +76 -28
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export { Head } from './meta/index.js';
|
|
|
17
17
|
export type { HeadProps } from './meta/index.js';
|
|
18
18
|
export { generateMeta, generateBreadcrumbs, generateArticleSchema, generateProductSchema, } from './meta/index.js';
|
|
19
19
|
export type { MetaData, OpenGraphData, TwitterCardData } from './meta/index.js';
|
|
20
|
-
export type { FC, RouteComponent, RouteChildren } from './runtime/types.js';
|
|
20
|
+
export type { FC, RouteComponent, RouteChildren, RouteParams } from './runtime/types.js';
|
|
21
21
|
export type { Logger, LoggerOptions, LogLevel, RequestLog, ResponseLog } from './logger/types.js';
|
|
22
22
|
export declare function defineConfig(config: Record<string, unknown>): Record<string, unknown>;
|
|
23
23
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,UAAU,CAAC;AAE/B,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EACL,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,KAAK,EACL,OAAO,EACP,MAAM,EACN,QAAQ,EACR,MAAM,GACP,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,KAAK,YAAY,EAAE,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC7F,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACnF,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC1E,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACjF,OAAO,EACL,oBAAoB,EACpB,aAAa,EACb,cAAc,EACd,kBAAkB,GACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAEjE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEzF,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEhF,YAAY,EAAE,EAAE,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,UAAU,CAAC;AAE/B,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EACL,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,KAAK,EACL,OAAO,EACP,MAAM,EACN,QAAQ,EACR,MAAM,GACP,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,KAAK,YAAY,EAAE,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC7F,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACnF,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC1E,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACjF,OAAO,EACL,oBAAoB,EACpB,aAAa,EACb,cAAc,EACd,kBAAkB,GACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAEjE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEzF,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEhF,YAAY,EAAE,EAAE,EAAE,cAAc,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEzF,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAElG,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAErF"}
|
|
@@ -126,10 +126,10 @@ export function emberkitVitePlugin(userOptions = {}) {
|
|
|
126
126
|
}
|
|
127
127
|
try {
|
|
128
128
|
const ssrModule = await server.ssrLoadModule(VIRTUAL_SSR_ENTRY);
|
|
129
|
-
const
|
|
130
|
-
res.statusCode = 200;
|
|
129
|
+
const result = await ssrModule.render(url, server);
|
|
130
|
+
res.statusCode = result.status || 200;
|
|
131
131
|
res.setHeader('Content-Type', 'text/html');
|
|
132
|
-
res.end(html);
|
|
132
|
+
res.end(result.html || result);
|
|
133
133
|
}
|
|
134
134
|
catch (error) {
|
|
135
135
|
server.ssrFixStacktrace(error);
|
|
@@ -1131,7 +1131,7 @@ function processParagraphs(html, breaks) {
|
|
|
1131
1131
|
}
|
|
1132
1132
|
function generateSSREntry() {
|
|
1133
1133
|
return `
|
|
1134
|
-
import { routes } from 'virtual:emberkit-routes';
|
|
1134
|
+
import { routes, notFoundRoute, errorRoute } from 'virtual:emberkit-routes';
|
|
1135
1135
|
import { createElement } from '@emberkit/core';
|
|
1136
1136
|
|
|
1137
1137
|
const matchRoute = (routes, pathname) => {
|
|
@@ -1170,12 +1170,12 @@ const renderToString = (element) => {
|
|
|
1170
1170
|
if (typeof element === 'string') return escapeHtml(element);
|
|
1171
1171
|
if (typeof element === 'number') return String(element);
|
|
1172
1172
|
if (Array.isArray(element)) return element.map(renderToString).join('');
|
|
1173
|
-
|
|
1173
|
+
|
|
1174
1174
|
if (typeof element !== 'object' || !element.type) return '';
|
|
1175
|
-
|
|
1175
|
+
|
|
1176
1176
|
let { type, props } = element;
|
|
1177
1177
|
props = props || {};
|
|
1178
|
-
|
|
1178
|
+
|
|
1179
1179
|
// Resolve function components
|
|
1180
1180
|
let depth = 0;
|
|
1181
1181
|
while (typeof type === 'function' && depth < 50) {
|
|
@@ -1197,22 +1197,22 @@ const renderToString = (element) => {
|
|
|
1197
1197
|
return '';
|
|
1198
1198
|
}
|
|
1199
1199
|
}
|
|
1200
|
-
|
|
1200
|
+
|
|
1201
1201
|
if (type === 'Fragment' || type === 'React.Fragment') {
|
|
1202
1202
|
const children = Array.isArray(props.children) ? props.children : [props.children];
|
|
1203
1203
|
return children.filter(Boolean).map(renderToString).join('');
|
|
1204
1204
|
}
|
|
1205
|
-
|
|
1205
|
+
|
|
1206
1206
|
const SELF_CLOSING = new Set(['area','base','br','col','embed','hr','img','input','link','meta','source','track','wbr']);
|
|
1207
|
-
|
|
1207
|
+
|
|
1208
1208
|
const children = Array.isArray(props.children) ? props.children : (props.children ? [props.children] : []);
|
|
1209
1209
|
let childHtml = children.filter(c => c != null).map(renderToString).join('');
|
|
1210
|
-
|
|
1210
|
+
|
|
1211
1211
|
// Handle dangerouslySetInnerHTML
|
|
1212
1212
|
if (props.dangerouslySetInnerHTML && props.dangerouslySetInnerHTML.__html) {
|
|
1213
1213
|
childHtml = props.dangerouslySetInnerHTML.__html;
|
|
1214
1214
|
}
|
|
1215
|
-
|
|
1215
|
+
|
|
1216
1216
|
const attrs = Object.entries(props)
|
|
1217
1217
|
.filter(([k, v]) => k !== 'children' && k !== 'key' && k !== 'dangerouslySetInnerHTML' && v != null && typeof v !== 'function')
|
|
1218
1218
|
.map(([k, v]) => {
|
|
@@ -1229,11 +1229,11 @@ const renderToString = (element) => {
|
|
|
1229
1229
|
return ' ' + k + '="' + escapeHtml(String(v)) + '"';
|
|
1230
1230
|
})
|
|
1231
1231
|
.join('');
|
|
1232
|
-
|
|
1232
|
+
|
|
1233
1233
|
if (SELF_CLOSING.has(type)) {
|
|
1234
1234
|
return '<' + type + attrs + '/>';
|
|
1235
1235
|
}
|
|
1236
|
-
|
|
1236
|
+
|
|
1237
1237
|
return '<' + type + attrs + '>' + childHtml + '</' + type + '>';
|
|
1238
1238
|
};
|
|
1239
1239
|
|
|
@@ -1249,24 +1249,25 @@ const escapeHtml = (str) => {
|
|
|
1249
1249
|
|
|
1250
1250
|
export async function render(url, server) {
|
|
1251
1251
|
const pathname = url.split('?')[0];
|
|
1252
|
-
|
|
1252
|
+
|
|
1253
1253
|
// Sort routes: static first, then dynamic
|
|
1254
1254
|
const sortedRoutes = [...routes].sort((a, b) => {
|
|
1255
1255
|
const aScore = a.path.includes(':') ? 0 : 1;
|
|
1256
1256
|
const bScore = b.path.includes(':') ? 0 : 1;
|
|
1257
1257
|
return bScore - aScore;
|
|
1258
1258
|
});
|
|
1259
|
-
|
|
1259
|
+
|
|
1260
1260
|
const match = matchRoute(sortedRoutes, pathname);
|
|
1261
|
-
|
|
1261
|
+
|
|
1262
1262
|
let appHtml = '';
|
|
1263
1263
|
let headContent = '';
|
|
1264
|
-
|
|
1264
|
+
let status = 200;
|
|
1265
|
+
|
|
1265
1266
|
if (match) {
|
|
1266
1267
|
try {
|
|
1267
1268
|
const mod = await match.route.component();
|
|
1268
1269
|
const Component = mod.default || mod;
|
|
1269
|
-
|
|
1270
|
+
|
|
1270
1271
|
// Get metadata if available
|
|
1271
1272
|
if (mod.metadata) {
|
|
1272
1273
|
if (mod.metadata.title) {
|
|
@@ -1276,24 +1277,56 @@ export async function render(url, server) {
|
|
|
1276
1277
|
headContent += '<meta name="description" content="' + escapeHtml(mod.metadata.description) + '">\\n';
|
|
1277
1278
|
}
|
|
1278
1279
|
}
|
|
1279
|
-
|
|
1280
|
+
|
|
1280
1281
|
const element = createElement(Component, { params: match.params });
|
|
1281
1282
|
appHtml = renderToString(element);
|
|
1282
1283
|
} catch (e) {
|
|
1283
1284
|
console.error('[SSR] Failed to render route:', pathname, e);
|
|
1284
|
-
|
|
1285
|
+
if (errorRoute) {
|
|
1286
|
+
try {
|
|
1287
|
+
status = 500;
|
|
1288
|
+
const mod = await errorRoute();
|
|
1289
|
+
const Component = mod.default || mod;
|
|
1290
|
+
const errorInfo = {
|
|
1291
|
+
status: 500,
|
|
1292
|
+
message: e instanceof Error ? e.message : 'Internal Server Error',
|
|
1293
|
+
error: e,
|
|
1294
|
+
};
|
|
1295
|
+
const element = createElement(Component, { error: errorInfo });
|
|
1296
|
+
appHtml = renderToString(element);
|
|
1297
|
+
} catch (fallbackError) {
|
|
1298
|
+
console.error('[SSR] Failed to render 500 page:', fallbackError);
|
|
1299
|
+
appHtml = '<div style="color: red; padding: 20px;">Internal Server Error</div>';
|
|
1300
|
+
}
|
|
1301
|
+
} else {
|
|
1302
|
+
appHtml = '<div style="color: red; padding: 20px;">SSR Error: ' + escapeHtml(String(e)) + '</div>';
|
|
1303
|
+
status = 500;
|
|
1304
|
+
}
|
|
1285
1305
|
}
|
|
1286
1306
|
} else {
|
|
1287
|
-
|
|
1307
|
+
status = 404;
|
|
1308
|
+
if (notFoundRoute) {
|
|
1309
|
+
try {
|
|
1310
|
+
const mod = await notFoundRoute();
|
|
1311
|
+
const Component = mod.default || mod;
|
|
1312
|
+
const element = createElement(Component, { });
|
|
1313
|
+
appHtml = renderToString(element);
|
|
1314
|
+
} catch (e) {
|
|
1315
|
+
console.error('[SSR] Failed to render 404 page:', e);
|
|
1316
|
+
appHtml = '<div style="padding: 20px;">404 - Page not found</div>';
|
|
1317
|
+
}
|
|
1318
|
+
} else {
|
|
1319
|
+
appHtml = '<div style="padding: 20px;">404 - Page not found</div>';
|
|
1320
|
+
}
|
|
1288
1321
|
}
|
|
1289
|
-
|
|
1322
|
+
|
|
1290
1323
|
// Load and transform index.html
|
|
1291
1324
|
const fs = await import('node:fs');
|
|
1292
1325
|
const path = await import('node:path');
|
|
1293
1326
|
const indexPath = path.join(server.config.root, 'index.html');
|
|
1294
1327
|
let template = fs.readFileSync(indexPath, 'utf-8');
|
|
1295
1328
|
template = await server.transformIndexHtml(url, template);
|
|
1296
|
-
|
|
1329
|
+
|
|
1297
1330
|
// Inject SSR content
|
|
1298
1331
|
// Look for body with id="app" or div with id="app"
|
|
1299
1332
|
if (template.includes('<body id="app">')) {
|
|
@@ -1303,13 +1336,13 @@ export async function render(url, server) {
|
|
|
1303
1336
|
} else if (template.includes('<div id="app"/>')) {
|
|
1304
1337
|
template = template.replace('<div id="app"/>', '<div id="app">' + appHtml + '</div>');
|
|
1305
1338
|
}
|
|
1306
|
-
|
|
1339
|
+
|
|
1307
1340
|
// Inject head content if any
|
|
1308
1341
|
if (headContent && template.includes('</head>')) {
|
|
1309
1342
|
template = template.replace('</head>', headContent + '</head>');
|
|
1310
1343
|
}
|
|
1311
|
-
|
|
1312
|
-
return template;
|
|
1344
|
+
|
|
1345
|
+
return { html: template, status };
|
|
1313
1346
|
}
|
|
1314
1347
|
`;
|
|
1315
1348
|
}
|
|
@@ -1360,10 +1393,23 @@ function scoreRoutePath(routePath) {
|
|
|
1360
1393
|
}
|
|
1361
1394
|
function generateRoutesCode(files, routeDir) {
|
|
1362
1395
|
const routeEntries = [];
|
|
1396
|
+
let notFoundRoute = 'null';
|
|
1397
|
+
let errorRoute = 'null';
|
|
1363
1398
|
for (const file of files) {
|
|
1364
1399
|
const relativePath = relative(routeDir, file).replace(/\\/g, '/');
|
|
1365
1400
|
const ext = file.split('.').pop() ?? '';
|
|
1366
1401
|
const isMarkdown = ext === 'md' || ext === 'mdx';
|
|
1402
|
+
// Check for special error pages
|
|
1403
|
+
if (relativePath === '404.tsx' || relativePath === '404.ts' || relativePath === '404.jsx' || relativePath === '404.js') {
|
|
1404
|
+
const importPath = file.replace(/\\/g, '/');
|
|
1405
|
+
notFoundRoute = `() => import(${JSON.stringify(importPath)})`;
|
|
1406
|
+
continue;
|
|
1407
|
+
}
|
|
1408
|
+
if (relativePath === '500.tsx' || relativePath === '500.ts' || relativePath === '500.jsx' || relativePath === '500.js') {
|
|
1409
|
+
const importPath = file.replace(/\\/g, '/');
|
|
1410
|
+
errorRoute = `() => import(${JSON.stringify(importPath)})`;
|
|
1411
|
+
continue;
|
|
1412
|
+
}
|
|
1367
1413
|
// Skip special files
|
|
1368
1414
|
if (relativePath.includes('_layout') ||
|
|
1369
1415
|
relativePath.includes('_error') ||
|
|
@@ -1394,5 +1440,7 @@ function generateRoutesCode(files, routeDir) {
|
|
|
1394
1440
|
// Sort static routes before dynamic routes so the emitted array already has
|
|
1395
1441
|
// the correct priority order (defense-in-depth alongside runtime scoring).
|
|
1396
1442
|
routeEntries.sort((a, b) => scoreRoutePath(b.path) - scoreRoutePath(a.path));
|
|
1397
|
-
return `export const routes = [\n${routeEntries.map((r) => r.entry).join(',\n')}\n]
|
|
1443
|
+
return `export const routes = [\n${routeEntries.map((r) => r.entry).join(',\n')}\n];
|
|
1444
|
+
export const notFoundRoute = ${notFoundRoute};
|
|
1445
|
+
export const errorRoute = ${errorRoute};`;
|
|
1398
1446
|
}
|