vike-lite 1.2.2 → 1.3.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 +7 -1
- package/dist/__internal/server.d.mts +0 -1
- package/dist/index.d.mts +15 -2
- package/dist/server.mjs +1 -1
- package/dist/vite.mjs +119 -9
- package/package.json +4 -12
- package/dist/index-B6dJFVs8.d.mts +0 -29
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Light version of [Vike](https://vike.dev)
|
|
|
4
4
|
|
|
5
5
|
### ✨ Features
|
|
6
6
|
❯ **Zero dependencies:**<br> _light_ with less dependencies and essential checks
|
|
7
|
-
❯ **Functionalities:**<br> [See documentation](doc/FUNCTIONALITIES)
|
|
7
|
+
❯ **Functionalities:**<br> [See documentation](doc/FUNCTIONALITIES.md)
|
|
8
8
|
|
|
9
9
|
### ⚙️ Install
|
|
10
10
|
| Package Manager | Command
|
|
@@ -65,6 +65,12 @@ app.onError((error, c) => {
|
|
|
65
65
|
export default app
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
+
> Note: if you don't use a custom server (loaded by the `serverEntry` option), a default server is bundled. In PROD the server is started as follow:
|
|
69
|
+
```ts
|
|
70
|
+
// startServer.mjs
|
|
71
|
+
import './dist/server/index.mjs'
|
|
72
|
+
```
|
|
73
|
+
|
|
68
74
|
### 🫶 Contribution
|
|
69
75
|
Clone
|
|
70
76
|
```sh
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
//#region src/index.d.ts
|
|
2
|
+
type PageContext<Data = unknown> = {
|
|
3
|
+
routeParams: Record<string, string>;
|
|
4
|
+
urlOriginal: string;
|
|
5
|
+
urlPathname: string;
|
|
6
|
+
data?: Data;
|
|
7
|
+
title?: string;
|
|
8
|
+
is404?: boolean;
|
|
9
|
+
is500?: boolean;
|
|
10
|
+
errorMessage?: string;
|
|
11
|
+
};
|
|
12
|
+
type DataAsync<Data = unknown> = (pageContext: PageContext) => Promise<Data>;
|
|
13
|
+
type DataSync<Data = unknown> = (pageContext: PageContext) => Data;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { DataAsync, DataSync, PageContext };
|
package/dist/server.mjs
CHANGED
package/dist/vite.mjs
CHANGED
|
@@ -2,7 +2,8 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { Readable } from "node:stream";
|
|
4
4
|
import { pipeline } from "node:stream/promises";
|
|
5
|
-
|
|
5
|
+
import { loadEnv } from "vite";
|
|
6
|
+
//#region src/utils/generateRoutes.ts
|
|
6
7
|
function generateRoutes(viteRoot, pagesDir) {
|
|
7
8
|
const pagesAbsPath = path.resolve(viteRoot, pagesDir);
|
|
8
9
|
if (!fs.existsSync(pagesAbsPath)) return { routes: [] };
|
|
@@ -55,6 +56,8 @@ function generateRoutes(viteRoot, pagesDir) {
|
|
|
55
56
|
errorRoute
|
|
56
57
|
};
|
|
57
58
|
}
|
|
59
|
+
//#endregion
|
|
60
|
+
//#region src/utils/injectFOUCStyles.ts
|
|
58
61
|
/**
|
|
59
62
|
* Anti-FOUC: Inspect all known SSR modules, ask the client environment
|
|
60
63
|
* to translate them into plain text (thanks to ?direct) and return them.
|
|
@@ -77,13 +80,18 @@ async function injectFOUCStyles(server, html) {
|
|
|
77
80
|
if (headEndIndex === -1) return html;
|
|
78
81
|
return `${html.slice(0, headEndIndex)}<style type="text/css" data-vite-dev-fouc>${cssContent}</style>${html.slice(headEndIndex)}`;
|
|
79
82
|
}
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region src/config.ts
|
|
85
|
+
const SUPPORTED_RENDERERS = ["solid"];
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/vite-plugin.ts
|
|
80
88
|
function routerPlugin({ pagesDir = "pages", serverEntry = "server/index", apiPrefix = "/api" } = {}) {
|
|
81
89
|
const isProd = process.env.NODE_ENV === "production";
|
|
82
90
|
let viteConfigRoot;
|
|
83
91
|
let outDir;
|
|
84
92
|
const virtualModuleId = "virtual:routes";
|
|
85
93
|
const virtualManifestId = "virtual:client-manifest";
|
|
86
|
-
const
|
|
94
|
+
const virtualRendererId = "virtual:vike-lite/renderer";
|
|
87
95
|
const virtualEntryClientId = "virtual:entry-client";
|
|
88
96
|
const virtualSetupId = "virtual:vike-lite/setup";
|
|
89
97
|
const virtualEntryServerId = "virtual:entry-server";
|
|
@@ -94,7 +102,9 @@ function routerPlugin({ pagesDir = "pages", serverEntry = "server/index", apiPre
|
|
|
94
102
|
const resolvedVirtualEntryServerId = "\0virtual:entry-server";
|
|
95
103
|
return {
|
|
96
104
|
name: "vike-lite",
|
|
97
|
-
config(config) {
|
|
105
|
+
config(config, { mode }) {
|
|
106
|
+
const envVariables = loadEnv(mode, config.envDir || process.cwd());
|
|
107
|
+
for (const key in envVariables) if (process.env[key] === void 0) process.env[key] = envVariables[key];
|
|
98
108
|
outDir = config.build?.outDir ?? "dist";
|
|
99
109
|
const { emptyOutDir, minify = true, cssMinify = true, sourcemap } = config.build || {};
|
|
100
110
|
return {
|
|
@@ -143,6 +153,7 @@ function routerPlugin({ pagesDir = "pages", serverEntry = "server/index", apiPre
|
|
|
143
153
|
},
|
|
144
154
|
configResolved(config) {
|
|
145
155
|
viteConfigRoot = config.root;
|
|
156
|
+
if (!config.plugins.some((plugin) => plugin.name?.startsWith("vike-lite-") && SUPPORTED_RENDERERS.includes(plugin.name.replace("vike-lite-", "")))) throw new Error(`No UI renderer plugin found in 'vite.config': please install and configure one of ${SUPPORTED_RENDERERS.map((r) => `vike-lite-${r}`).join(", ")}`);
|
|
146
157
|
},
|
|
147
158
|
resolveId(id) {
|
|
148
159
|
if (id === virtualModuleId) return resolvedVirtualModuleId;
|
|
@@ -155,7 +166,7 @@ function routerPlugin({ pagesDir = "pages", serverEntry = "server/index", apiPre
|
|
|
155
166
|
if (id === resolvedVirtualModuleId) {
|
|
156
167
|
const { routes, errorRoute } = generateRoutes(viteConfigRoot, pagesDir);
|
|
157
168
|
const isSSR = options.ssr;
|
|
158
|
-
let code = `import { onRenderHtml } from '${
|
|
169
|
+
let code = `import { onRenderHtml } from '${virtualRendererId}';\nexport const config = { onRenderHtml };\nexport const routes = [\n`;
|
|
159
170
|
for (const r of routes) {
|
|
160
171
|
code += `{path:'${r.path}',page:'${r.page}',Page:()=>import('/${r.page}'),`;
|
|
161
172
|
if (r.head) code += `head:'${r.head}',Head:()=>import('/${r.head}'),`;
|
|
@@ -183,7 +194,7 @@ function routerPlugin({ pagesDir = "pages", serverEntry = "server/index", apiPre
|
|
|
183
194
|
return `export default ${fs.readFileSync(manifestPath, "utf8")}`;
|
|
184
195
|
}
|
|
185
196
|
if (id === resolvedVirtualEntryClientId) return `import { routes, errorRoute } from '${virtualModuleId}';
|
|
186
|
-
import { onRenderClient } from '${
|
|
197
|
+
import { onRenderClient } from '${virtualRendererId}';
|
|
187
198
|
const { default: render } = await onRenderClient();
|
|
188
199
|
await render({ routes, errorRoute });`;
|
|
189
200
|
if (id === resolvedVirtualSetupId) return `
|
|
@@ -196,10 +207,109 @@ function routerPlugin({ pagesDir = "pages", serverEntry = "server/index", apiPre
|
|
|
196
207
|
setVikeState({ routes, errorRoute, config, manifest });
|
|
197
208
|
`;
|
|
198
209
|
if (id === resolvedVirtualEntryServerId) {
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
210
|
+
const extensions = [
|
|
211
|
+
".ts",
|
|
212
|
+
".js",
|
|
213
|
+
".mjs"
|
|
214
|
+
];
|
|
215
|
+
let hasCustomServer = false;
|
|
216
|
+
let customServerPath = "";
|
|
217
|
+
if (serverEntry) {
|
|
218
|
+
const basePath = path.resolve(viteConfigRoot, serverEntry);
|
|
219
|
+
for (const ext of ["", ...extensions]) if (fs.existsSync(basePath + ext)) {
|
|
220
|
+
hasCustomServer = true;
|
|
221
|
+
customServerPath = (basePath + ext).replaceAll("\\", "/");
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (hasCustomServer) return `import '${virtualSetupId}';
|
|
226
|
+
export * from '${customServerPath}';
|
|
227
|
+
export { default } from '${customServerPath}';`;
|
|
228
|
+
return String.raw`import '${virtualSetupId}';
|
|
229
|
+
import { renderPage } from 'vike-lite/server';
|
|
230
|
+
export default {
|
|
231
|
+
async fetch(request) {
|
|
232
|
+
return renderPage(request);
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
if (process.env.NODE_ENV === 'production') {
|
|
236
|
+
const { createServer } = await import('node:http');
|
|
237
|
+
const { Readable } = await import('node:stream');
|
|
238
|
+
const { pipeline } = await import('node:stream/promises');
|
|
239
|
+
const fs = await import('node:fs');
|
|
240
|
+
const path = await import('node:path');
|
|
241
|
+
const { fileURLToPath } = await import('node:url');
|
|
242
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
243
|
+
const clientDir = path.resolve(__dirname, '../client');
|
|
244
|
+
const MIME_TYPES = {
|
|
245
|
+
'.html': 'text/html',
|
|
246
|
+
'.js': 'text/javascript',
|
|
247
|
+
'.mjs': 'text/javascript',
|
|
248
|
+
'.css': 'text/css',
|
|
249
|
+
'.json': 'application/json',
|
|
250
|
+
'.png': 'image/png',
|
|
251
|
+
'.jpg': 'image/jpeg',
|
|
252
|
+
'.jpeg': 'image/jpeg',
|
|
253
|
+
'.gif': 'image/gif',
|
|
254
|
+
'.svg': 'image/svg+xml',
|
|
255
|
+
'.ico': 'image/x-icon',
|
|
256
|
+
'.webmanifest': 'application/manifest+json',
|
|
257
|
+
'.xml': 'application/xml',
|
|
258
|
+
'.txt': 'text/plain',
|
|
259
|
+
'.woff': 'font/woff',
|
|
260
|
+
'.woff2': 'font/woff2'
|
|
261
|
+
};
|
|
262
|
+
const server = createServer(async (req, res) => {
|
|
263
|
+
try {
|
|
264
|
+
const urlObj = new URL(req.url || '/', 'http://' + (req.headers.host || 'localhost'));
|
|
265
|
+
const pathname = urlObj.pathname;
|
|
266
|
+
if (pathname !== '/') {
|
|
267
|
+
const filePath = path.join(clientDir, pathname);
|
|
268
|
+
if (filePath.startsWith(clientDir) && fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
269
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
270
|
+
const mimeType = MIME_TYPES[ext] || 'application/octet-stream';
|
|
271
|
+
res.setHeader('Content-Type', mimeType);
|
|
272
|
+
if (pathname.startsWith('/assets/')) {
|
|
273
|
+
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
|
|
274
|
+
}
|
|
275
|
+
fs.createReadStream(filePath).pipe(res);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
const headers = new Headers();
|
|
280
|
+
for (const [key, val] of Object.entries(req.headers)) {
|
|
281
|
+
if (Array.isArray(val)) val.forEach(v => headers.append(key, v));
|
|
282
|
+
else if (val) headers.set(key, val);
|
|
283
|
+
}
|
|
284
|
+
const { method } = req;
|
|
285
|
+
const init = { method, headers };
|
|
286
|
+
if (method !== 'GET' && method !== 'HEAD') {
|
|
287
|
+
init.body = Readable.toWeb(req);
|
|
288
|
+
init.duplex = 'half';
|
|
289
|
+
}
|
|
290
|
+
const request = new Request(urlObj.href, init);
|
|
291
|
+
const response = await renderPage(request);
|
|
292
|
+
res.statusCode = response.status;
|
|
293
|
+
for (const [key, val] of response.headers) {
|
|
294
|
+
res.setHeader(key, val);
|
|
295
|
+
}
|
|
296
|
+
if (response.body) {
|
|
297
|
+
await pipeline(Readable.fromWeb(response.body), res);
|
|
298
|
+
} else {
|
|
299
|
+
res.end();
|
|
300
|
+
}
|
|
301
|
+
} catch (e) {
|
|
302
|
+
console.error(e);
|
|
303
|
+
res.statusCode = 500;
|
|
304
|
+
res.end('Internal Server Error');
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
const { PORT } = process.env;
|
|
308
|
+
const port = PORT ? Number.parseInt(PORT) : 3000;
|
|
309
|
+
server.listen(port, () => {
|
|
310
|
+
console.log('\n\x1b[32m🚀 Server is running at http://localhost:' + port + '\x1b[0m\n');
|
|
311
|
+
});
|
|
312
|
+
}`;
|
|
203
313
|
}
|
|
204
314
|
},
|
|
205
315
|
configureServer(server) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vike-lite",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -38,18 +38,11 @@
|
|
|
38
38
|
"dist"
|
|
39
39
|
],
|
|
40
40
|
"scripts": {
|
|
41
|
-
"
|
|
42
|
-
"build": "tsdown",
|
|
43
|
-
"od": "yarn outdated"
|
|
41
|
+
"build": "tsdown"
|
|
44
42
|
},
|
|
45
43
|
"devDependencies": {
|
|
46
44
|
"@types/node": "^26.0.1",
|
|
47
|
-
"@yarnpkg/sdks": "^3.3.0",
|
|
48
|
-
"eslint": "^10.6.0",
|
|
49
|
-
"eslint-plugin-unicorn": "^69.0.0",
|
|
50
45
|
"tsdown": "^0.22.3",
|
|
51
|
-
"typescript": "^6.0.3",
|
|
52
|
-
"typescript-eslint": "^8.62.0",
|
|
53
46
|
"vite": "^8.1.0"
|
|
54
47
|
},
|
|
55
48
|
"peerDependencies": {
|
|
@@ -57,6 +50,5 @@
|
|
|
57
50
|
},
|
|
58
51
|
"engines": {
|
|
59
52
|
"node": "^20.19.0 || >=22.12.0"
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
//#region src/index.d.ts
|
|
2
|
-
type PageContext<Data = unknown> = {
|
|
3
|
-
routeParams: Record<string, string>;
|
|
4
|
-
urlOriginal: string;
|
|
5
|
-
urlPathname: string;
|
|
6
|
-
data?: Data;
|
|
7
|
-
title?: string;
|
|
8
|
-
is404?: boolean;
|
|
9
|
-
is500?: boolean;
|
|
10
|
-
errorMessage?: string;
|
|
11
|
-
};
|
|
12
|
-
type DataAsync<Data = unknown> = (pageContext: PageContext) => Promise<Data>;
|
|
13
|
-
type DataSync<Data = unknown> = (pageContext: PageContext) => Data;
|
|
14
|
-
type Route = {
|
|
15
|
-
path: string;
|
|
16
|
-
page: string;
|
|
17
|
-
layout?: string;
|
|
18
|
-
head?: string;
|
|
19
|
-
hasData?: boolean;
|
|
20
|
-
hasTitle?: boolean;
|
|
21
|
-
};
|
|
22
|
-
type Manifest = Record<string, {
|
|
23
|
-
file: string;
|
|
24
|
-
css?: string[];
|
|
25
|
-
imports?: string[];
|
|
26
|
-
isEntry?: boolean;
|
|
27
|
-
}>;
|
|
28
|
-
//#endregion
|
|
29
|
-
export { Route as a, PageContext as i, DataSync as n, Manifest as r, DataAsync as t };
|