vike-lite 1.14.1 → 1.15.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 CHANGED
@@ -1,4 +1,5 @@
1
- # vike-lite
1
+ # Vike Lite
2
+ <a href="https://npmjs.com/package/vike-lite"><img src="https://img.shields.io/npm/v/vike-lite.svg" alt="npm package"></a>
2
3
 
3
4
  A lightweight, fast, and minimal framework for Server-Side Rendering (SSR) and Static Site Generation (SSG) inspired by [Vike](https://vike.dev).
4
5
 
@@ -27,9 +28,9 @@ export default {
27
28
  plugins: [
28
29
  vikeLite({
29
30
  pagesDir: 'pages', // Default: Directory containing your pages
30
- serverEntry: 'server/index', // Default: Custom server entry file
31
31
  apiPrefix: '/api', // Default: Prefix to bypass SSR for API routes
32
32
  prerender: false // Default: Enable SSG globally
33
+ serverEntry: 'server/index', // Default: unfedined value that allows to use a custom server entry file
33
34
  })
34
35
  ]
35
36
  } satisfies UserConfig
@@ -61,9 +62,8 @@ app.route('/api', apiRoutes)
61
62
 
62
63
  // 2. Catch-all remaining requests and pass them to vike-lite
63
64
  app.get('*', async (c, next) => {
64
- const response = await renderPage(c.req.raw)
65
- // If vike-lite successfully renders a page, return it. Otherwise, continue.
66
- return response ?? next()
65
+ // renderPage will return a Node.js Response
66
+ return await renderPage(c.req.raw)
67
67
  })
68
68
 
69
69
  // 3. Error Handling
@@ -0,0 +1,83 @@
1
+ import { renderPage } from 'vike-lite/server'
2
+ import { createServer } from 'node:http'
3
+ import { Readable } from 'node:stream'
4
+ import { pipeline } from 'node:stream/promises'
5
+ import fs from 'node:fs'
6
+ import fsPromises from 'node:fs/promises'
7
+ import path from 'node:path'
8
+ import { fileURLToPath } from 'node:url'
9
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
10
+ const clientDir = path.resolve(__dirname, '../client')
11
+ const MIME_TYPES = {
12
+ '.html': 'text/html; charset=utf-8', '.js': 'text/javascript; charset=utf-8',
13
+ '.mjs': 'text/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8',
14
+ '.json': 'application/json; charset=utf-8', '.xml': 'application/xml',
15
+ '.txt': 'text/plain; charset=utf-8', '.map': 'application/json; charset=utf-8',
16
+ '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg',
17
+ '.gif': 'image/gif', '.svg': 'image/svg+xml', '.ico': 'image/x-icon',
18
+ '.webp': 'image/webp', '.avif': 'image/avif', '.woff': 'font/woff',
19
+ '.woff2': 'font/woff2', '.ttf': 'font/ttf', '.otf': 'font/otf',
20
+ '.mp4': 'video/mp4', '.webm': 'video/webm', '.mp3': 'audio/mpeg',
21
+ '.pdf': 'application/pdf', '.wasm': 'application/wasm',
22
+ '.webmanifest': 'application/manifest+json'
23
+ }
24
+ const server = createServer(async (req, res) => {
25
+ try {
26
+ const urlObj = new URL(req.url || '/', 'http://' + (req.headers.host || 'localhost'))
27
+ const pathname = urlObj.pathname
28
+ let fileUrl = pathname
29
+ const { BASE_URL } = import.meta.env
30
+ if (BASE_URL !== '/' && pathname.startsWith(BASE_URL)) fileUrl = '/' + pathname.slice(BASE_URL.length)
31
+ if (fileUrl !== '/') {
32
+ const filePath = path.resolve(clientDir, '.' + fileUrl)
33
+ if (!filePath.startsWith(clientDir)) {
34
+ res.statusCode = 403
35
+ res.end('Forbidden')
36
+ return
37
+ }
38
+ try {
39
+ const stat = await fsPromises.stat(filePath)
40
+ if (stat.isFile()) {
41
+ const ext = path.extname(filePath).toLowerCase()
42
+ const mimeType = MIME_TYPES[ext] || 'application/octet-stream'
43
+ res.setHeader('Content-Type', mimeType)
44
+ res.setHeader(
45
+ 'Cache-Control',
46
+ fileUrl.startsWith('/assets/') ? 'public, max-age=31536000, immutable' : 'public, max-age=0, must-revalidate'
47
+ )
48
+ fs.createReadStream(filePath).pipe(res)
49
+ return
50
+ }
51
+ } catch { }
52
+ }
53
+ const headers = new Headers()
54
+ for (const [key, val] of Object.entries(req.headers)) {
55
+ if (Array.isArray(val)) for (const v of val) headers.append(key, v)
56
+ else if (val) headers.set(key, val)
57
+ }
58
+ const { method } = req
59
+ const init = { method, headers }
60
+ if (method !== 'GET' && method !== 'HEAD') {
61
+ init.body = Readable.toWeb(req)
62
+ init.duplex = 'half'
63
+ }
64
+ const request = new Request(urlObj.href, init)
65
+ const response = await renderPage(request)
66
+ res.statusCode = response.status
67
+ for (const [key, val] of response.headers) res.setHeader(key, val)
68
+ if (method === 'HEAD' || !response.body) {
69
+ await response.body?.cancel()
70
+ res.end()
71
+ return
72
+ }
73
+ try { await pipeline(Readable.fromWeb(response.body), res) } catch (error) { console.error('Stream error:', error) }
74
+ } catch (error) {
75
+ console.error('Handler error:', error)
76
+ if (!res.headersSent) {
77
+ res.statusCode = 500
78
+ res.end('Internal Server Error')
79
+ }
80
+ }
81
+ })
82
+ const port = process.env.PORT ? Number.parseInt(process.env.PORT) : 3000
83
+ server.listen(port, () => { console.log(`\n\u{1B}[32m🚀 Server is running at http://localhost:${port}\u{1B}[0m\n`) })
@@ -117,7 +117,11 @@ async function renderErrorPage(req, status, urlPathname, error, nonce) {
117
117
  errorMessage = isProd ? "Internal Server Error" : error instanceof Error ? error.message : "Unknown error";
118
118
  is500 = true;
119
119
  } else is500 = false;
120
- if (!store.errorRoute) return new Response(status === 404 ? "Not Found" : "Internal Server Error", { status });
120
+ const fallbackText = status === 404 ? "Not Found" : "Internal Server Error";
121
+ if (!store.errorRoute) return new Response(fallbackText, {
122
+ status,
123
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
124
+ });
121
125
  try {
122
126
  const [PageModule, HeadModule, LayoutModule] = await Promise.all([
123
127
  store.errorRoute.Page(),
@@ -144,11 +148,14 @@ async function renderErrorPage(req, status, urlPathname, error, nonce) {
144
148
  });
145
149
  return new Response(html, {
146
150
  status,
147
- headers: { "Content-Type": "text/html" }
151
+ headers: { "Content-Type": "text/html; charset=utf-8" }
148
152
  });
149
153
  } catch (renderError) {
150
154
  console.error("[vike-lite] Error page render failed:", renderError);
151
- return new Response(status === 404 ? "Not Found" : "Internal Server Error", { status });
155
+ return new Response(fallbackText, {
156
+ status,
157
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
158
+ });
152
159
  }
153
160
  }
154
161
  async function renderPage(req, { nonce } = {}) {
@@ -185,7 +192,7 @@ async function renderPage(req, { nonce } = {}) {
185
192
  });
186
193
  return new Response(html, {
187
194
  status: 200,
188
- headers: { "Content-Type": "text/html" }
195
+ headers: { "Content-Type": "text/html; charset=utf-8" }
189
196
  });
190
197
  } catch (error) {
191
198
  if (error instanceof AbortRedirect) {
package/dist/server.d.mts CHANGED
@@ -1,6 +1,7 @@
1
1
  //#region src/server/renderPage.d.ts
2
2
  declare function renderPage(req: Request, { nonce }?: {
3
3
  nonce?: string;
4
+ [key: string]: any;
4
5
  }): Promise<Response>;
5
6
  //#endregion
6
7
  export { renderPage };
package/dist/server.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { t as renderPage } from "./server-BkIWABsi.mjs";
1
+ import { t as renderPage } from "./server-BITt1YvS.mjs";
2
2
  export { renderPage };
package/dist/vite.d.mts CHANGED
@@ -1,19 +1,12 @@
1
1
  import { Plugin } from "vite";
2
2
  //#region src/vite/index.d.ts
3
- declare function vikeLite({ pagesDir, serverEntry, apiPrefix, prerender }?: {
3
+ declare function vikeLite({ pagesDir, apiPrefix, prerender, serverEntry }?: {
4
4
  /**
5
5
  * The directory where your page components are located.
6
6
  * This is where the plugin will look for your page files to generate routes.
7
7
  * @default 'pages'
8
8
  */
9
9
  pagesDir?: string;
10
- /**
11
- * The entry point for your server application code.
12
- * This is where you can define custom server logic, such as API routes or middleware.
13
- * The build will produce dist/server/index.mjs, which is the entry point for your server application.
14
- * @default 'server/index'
15
- */
16
- serverEntry?: string;
17
10
  /**
18
11
  * The prefix for your API routes.
19
12
  * @default '/api'
@@ -25,6 +18,14 @@ declare function vikeLite({ pagesDir, serverEntry, apiPrefix, prerender }?: {
25
18
  * @default false
26
19
  */
27
20
  prerender?: boolean;
21
+ /**
22
+ * The entry point for your server application code.
23
+ * This is where you can define custom server logic, such as API routes or middleware.
24
+ * The build will produce dist/server/index.mjs, which is the entry point for your server application.
25
+ * If false disable the server entry.
26
+ * @default undefined
27
+ */
28
+ serverEntry?: string | false;
28
29
  }): Plugin;
29
30
  //#endregion
30
31
  export { vikeLite as default };
package/dist/vite.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as renderPage } from "./server-BkIWABsi.mjs";
1
+ import { t as renderPage } from "./server-BITt1YvS.mjs";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import { Readable } from "node:stream";
@@ -103,7 +103,7 @@ const SUPPORTED_RENDERERS = [
103
103
  ];
104
104
  //#endregion
105
105
  //#region src/vite/index.ts
106
- function vikeLite({ pagesDir = "pages", serverEntry = "server/index", apiPrefix = "/api", prerender = false } = {}) {
106
+ function vikeLite({ pagesDir = "pages", apiPrefix = "/api", prerender = false, serverEntry } = {}) {
107
107
  const isProd = process.env.NODE_ENV === "production";
108
108
  let viteConfigRoot;
109
109
  let outDir;
@@ -120,6 +120,7 @@ function vikeLite({ pagesDir = "pages", serverEntry = "server/index", apiPrefix
120
120
  };
121
121
  const VIRTUAL_VALUES = new Set(Object.values(VIRTUAL));
122
122
  const RESOLVED = Object.fromEntries(Object.entries(VIRTUAL).map(([k, v]) => [k, `\0${v}`]));
123
+ const importSetup = `import'${VIRTUAL.setup}';`;
123
124
  return {
124
125
  name: "vike-lite",
125
126
  config(config, { mode }) {
@@ -127,7 +128,7 @@ function vikeLite({ pagesDir = "pages", serverEntry = "server/index", apiPrefix
127
128
  for (const key in envVariables) if (process.env[key] === void 0) process.env[key] = envVariables[key];
128
129
  outDir = config.build?.outDir ?? "dist";
129
130
  const { emptyOutDir, minify = true, cssMinify = true, sourcemap } = config.build || {};
130
- viteConfigRoot = config.root ? path.resolve(config.root) : process.cwd();
131
+ viteConfigRoot = (config.root ? path.resolve(config.root) : process.cwd()).replaceAll("\\", "/");
131
132
  const { routes } = generateRoutes(viteConfigRoot, pagesDir);
132
133
  hasAnyPrerender = prerender || routes.some((r) => r.prerender);
133
134
  return {
@@ -247,7 +248,8 @@ function vikeLite({ pagesDir = "pages", serverEntry = "server/index", apiPrefix
247
248
  return code;
248
249
  }
249
250
  if (id === RESOLVED.manifest) {
250
- if (!isProd || !options?.ssr) return "export default{}";
251
+ const isSSR = options.ssr;
252
+ if (!isProd || !isSSR) return "export default{}";
251
253
  const manifestPath = path.join(viteConfigRoot, outDir, "client/.vite/manifest.json");
252
254
  return `export default ${fs.readFileSync(manifestPath, "utf8")}`;
253
255
  }
@@ -258,28 +260,31 @@ function vikeLite({ pagesDir = "pages", serverEntry = "server/index", apiPrefix
258
260
  }
259
261
  if (id === RESOLVED.entryServer) {
260
262
  if (serverEntry) {
261
- const basePath = path.resolve(viteConfigRoot, serverEntry);
263
+ const basePath = path.join(viteConfigRoot, serverEntry);
262
264
  let serverEntryPath = "";
263
- for (const ext of [
264
- ".ts",
265
- ".js",
266
- ".mjs"
267
- ]) if (fs.existsSync(basePath + ext)) {
268
- serverEntryPath = (basePath + ext).replaceAll("\\", "/");
269
- break;
265
+ if (!fs.existsSync(basePath)) {
266
+ for (const ext of [
267
+ ".ts",
268
+ ".js",
269
+ ".mjs"
270
+ ]) if (fs.existsSync(basePath + ext)) {
271
+ serverEntryPath = basePath + ext;
272
+ break;
273
+ }
274
+ if (!serverEntryPath) throw new Error(`[vike-lite] serverEntry ${serverEntry} file not found`);
270
275
  }
271
- if (!serverEntryPath) throw new Error(`[vike-lite] serverEntry ${serverEntry} file not found`);
272
- return `import '${VIRTUAL.setup}';export * from'${serverEntryPath}';export{default}from'${serverEntryPath}';`;
276
+ return importSetup + `export*from'${serverEntryPath}';export{default}from'${serverEntryPath}';`;
273
277
  }
274
- const defaultServerEntryContent = isProd ? fs.readFileSync(path.resolve(viteConfigRoot, "defaultServerEntry.mjs"), "utf8") : `import{renderPage}from'vike-lite/server'\n`;
275
- return `import'${VIRTUAL.setup}'\n` + defaultServerEntryContent + "export default{fetch:renderPage}\n";
278
+ if (serverEntry === false) return importSetup + "import{renderPage}from'vike-lite/server';export default{fetch:renderPage};";
279
+ const defaultServerEntryContent = isProd ? fs.readFileSync(path.resolve("vike-lite/__internal/vite/defaultServerEntry.mjs"), "utf8") : `import{renderPage}from'vike-lite/server';`;
280
+ return importSetup + defaultServerEntryContent + "export default{fetch:renderPage};";
276
281
  }
277
- if (id === RESOLVED.entryPrerender) return `import'${VIRTUAL.setup}';export{routes}from'${VIRTUAL.routes}';`;
282
+ if (id === RESOLVED.entryPrerender) return importSetup + `export{routes}from'${VIRTUAL.routes}';`;
278
283
  },
279
284
  async closeBundle() {
280
285
  if (!isProd || this.environment.name !== "ssr") return;
281
286
  const { pathToFileURL } = await import("node:url");
282
- const prerenderPath = path.resolve(viteConfigRoot, outDir, "server/prerender.mjs");
287
+ const prerenderPath = path.join(viteConfigRoot, outDir, "server/prerender.mjs");
283
288
  if (!fs.existsSync(prerenderPath)) return;
284
289
  const { routes } = await import(pathToFileURL(prerenderPath).href);
285
290
  if (!routes) return;
@@ -312,7 +317,7 @@ function vikeLite({ pagesDir = "pages", serverEntry = "server/index", apiPrefix
312
317
  const { BASE_URL } = import.meta.env;
313
318
  const baseNoSlash = BASE_URL.endsWith("/") ? BASE_URL.slice(0, -1) : BASE_URL;
314
319
  let generatedCount = 0;
315
- const clientDir = path.resolve(viteConfigRoot, outDir, "client");
320
+ const clientDir = path.join(viteConfigRoot, outDir, "client");
316
321
  for (const urlPath of urlsToPrerender) {
317
322
  const htmlRes = await renderPage(new Request(`http://localhost${baseNoSlash}${urlPath}`));
318
323
  if (htmlRes?.ok && htmlRes.headers.get("content-type")?.includes("text/html")) {
@@ -334,7 +339,7 @@ function vikeLite({ pagesDir = "pages", serverEntry = "server/index", apiPrefix
334
339
  },
335
340
  configureServer(server) {
336
341
  return () => {
337
- const pagesPath = path.resolve(viteConfigRoot, pagesDir);
342
+ const pagesPath = path.join(viteConfigRoot, pagesDir);
338
343
  server.watcher.on("all", (event, file) => {
339
344
  if (!((event === "add" || event === "unlink") && file.startsWith(pagesPath))) return;
340
345
  for (const env of Object.values(server.environments)) {
package/package.json CHANGED
@@ -1,7 +1,38 @@
1
1
  {
2
2
  "name": "vike-lite",
3
- "version": "1.14.1",
3
+ "version": "1.15.1",
4
4
  "license": "MIT",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/node-ecosystem/vike-lite.git"
8
+ },
9
+ "homepage": "https://github.com/node-ecosystem/vike-lite#readme",
10
+ "bugs": {
11
+ "url": "https://github.com/node-ecosystem/vike-lite/issues"
12
+ },
13
+ "keywords": [
14
+ "framework",
15
+ "lite",
16
+ "react",
17
+ "react-ssr",
18
+ "server-side-rendering",
19
+ "solid",
20
+ "solid-js",
21
+ "solid-ssr",
22
+ "solidjs",
23
+ "ssg",
24
+ "ssr",
25
+ "static-site-generator",
26
+ "vike",
27
+ "vike-lite",
28
+ "vite",
29
+ "vite-plugin",
30
+ "vite-ssr",
31
+ "vitejs",
32
+ "vue",
33
+ "vue-ssr",
34
+ "web-framework"
35
+ ],
5
36
  "type": "module",
6
37
  "module": "./dist/index.mjs",
7
38
  "types": "./dist/index.d.mts",
@@ -34,14 +65,6 @@
34
65
  "import": "./dist/__internal/server.mjs"
35
66
  }
36
67
  },
37
- "repository": {
38
- "type": "git",
39
- "url": "git+https://github.com/node-ecosystem/vike-lite.git"
40
- },
41
- "homepage": "https://github.com/node-ecosystem/vike-lite#readme",
42
- "bugs": {
43
- "url": "https://github.com/node-ecosystem/vike-lite/issues"
44
- },
45
68
  "files": [
46
69
  "dist"
47
70
  ],
@@ -51,7 +74,7 @@
51
74
  },
52
75
  "devDependencies": {
53
76
  "@types/node": "^26.1.1",
54
- "tsdown": "^0.22.7",
77
+ "tsdown": "^0.22.8",
55
78
  "vite": "^8.1.4"
56
79
  },
57
80
  "peerDependencies": {