vike-lite 1.5.0 → 1.6.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/server.mjs +14 -5
- package/dist/vite.d.mts +18 -3
- package/dist/vite.mjs +53 -33
- package/package.json +1 -1
package/dist/server.mjs
CHANGED
|
@@ -26,11 +26,15 @@ var AbortRender = class extends Error {
|
|
|
26
26
|
//#endregion
|
|
27
27
|
//#region src/server/renderPage.ts
|
|
28
28
|
const isProd = process.env.NODE_ENV === "production";
|
|
29
|
+
const { BASE_URL } = import.meta.env;
|
|
30
|
+
function withBase(file) {
|
|
31
|
+
return `${BASE_URL.replace(/\/$/, "")}/${file.replace(/^\//, "")}`;
|
|
32
|
+
}
|
|
29
33
|
function getAssets(route) {
|
|
30
34
|
if (!isProd) return {
|
|
31
35
|
cssLinks: "",
|
|
32
36
|
jsPreloads: "",
|
|
33
|
-
entryClient: "
|
|
37
|
+
entryClient: withBase("@id/virtual:entry-client")
|
|
34
38
|
};
|
|
35
39
|
const cssFiles = /* @__PURE__ */ new Set();
|
|
36
40
|
const jsFiles = /* @__PURE__ */ new Set();
|
|
@@ -55,9 +59,9 @@ function getAssets(route) {
|
|
|
55
59
|
if (head) collectAssets(head);
|
|
56
60
|
if (layout) collectAssets(layout);
|
|
57
61
|
return {
|
|
58
|
-
cssLinks: [...cssFiles].map((href) => `<link rel="stylesheet" href="
|
|
59
|
-
jsPreloads: [...jsFiles].map((href) => `<link rel="modulepreload" href="
|
|
60
|
-
entryClient:
|
|
62
|
+
cssLinks: [...cssFiles].map((href) => `<link rel="stylesheet" href="${withBase(href)}">`).join(""),
|
|
63
|
+
jsPreloads: [...jsFiles].map((href) => `<link rel="modulepreload" href="${withBase(href)}">`).join(""),
|
|
64
|
+
entryClient: withBase(manifest[virtualEntryClientKey].file)
|
|
61
65
|
};
|
|
62
66
|
}
|
|
63
67
|
async function buildPageContext(urlPathname, urlOriginal, isJsonRequest) {
|
|
@@ -133,7 +137,12 @@ async function renderErrorPage(req, status, urlPathname, error) {
|
|
|
133
137
|
}
|
|
134
138
|
}
|
|
135
139
|
async function renderPage(req) {
|
|
136
|
-
|
|
140
|
+
let pathname = new URL(req.url).pathname;
|
|
141
|
+
if (BASE_URL !== "/") {
|
|
142
|
+
const baseSlashed = BASE_URL.endsWith("/") ? BASE_URL : BASE_URL + "/";
|
|
143
|
+
const baseNoSlash = baseSlashed.slice(0, -1);
|
|
144
|
+
pathname = pathname === baseNoSlash ? "/" : pathname.slice(baseSlashed.length - 1);
|
|
145
|
+
}
|
|
137
146
|
const isJsonRequest = pathname.endsWith(".pageContext.json");
|
|
138
147
|
let targetPathname = pathname;
|
|
139
148
|
if (isJsonRequest) {
|
package/dist/vite.d.mts
CHANGED
|
@@ -6,9 +6,24 @@ declare function routerPlugin({
|
|
|
6
6
|
serverEntry,
|
|
7
7
|
apiPrefix
|
|
8
8
|
}?: {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
/**
|
|
10
|
+
* The directory where your page components are located.
|
|
11
|
+
* This is where the plugin will look for your page files to generate routes.
|
|
12
|
+
* @default 'pages'
|
|
13
|
+
*/
|
|
14
|
+
pagesDir?: string;
|
|
15
|
+
/**
|
|
16
|
+
* The entry point for your server application code.
|
|
17
|
+
* This is where you can define custom server logic, such as API routes or middleware.
|
|
18
|
+
* The build will produce dist/server/index.mjs, which is the entry point for your server application.
|
|
19
|
+
* @default 'server/index'
|
|
20
|
+
*/
|
|
21
|
+
serverEntry?: string;
|
|
22
|
+
/**
|
|
23
|
+
* The prefix for your API routes.
|
|
24
|
+
* @default '/api'
|
|
25
|
+
*/
|
|
26
|
+
apiPrefix?: string;
|
|
12
27
|
}): Plugin;
|
|
13
28
|
//#endregion
|
|
14
29
|
export { routerPlugin as default };
|
package/dist/vite.mjs
CHANGED
|
@@ -4,6 +4,11 @@ import { Readable } from "node:stream";
|
|
|
4
4
|
import { pipeline } from "node:stream/promises";
|
|
5
5
|
import { loadEnv } from "vite";
|
|
6
6
|
//#region src/utils/generateRoutes.ts
|
|
7
|
+
const pageExtensions = [".ts", ".js"];
|
|
8
|
+
const pageExtensionsX = [".tsx", ".jsx"];
|
|
9
|
+
function findFile(files, basename, extensions) {
|
|
10
|
+
return files.find((file) => extensions.some((ext) => file === `${basename}${ext}`));
|
|
11
|
+
}
|
|
7
12
|
function generateRoutes(viteRoot, pagesDir) {
|
|
8
13
|
const pagesAbsPath = path.resolve(viteRoot, pagesDir);
|
|
9
14
|
if (!fs.existsSync(pagesAbsPath)) return { routes: [] };
|
|
@@ -12,15 +17,22 @@ function generateRoutes(viteRoot, pagesDir) {
|
|
|
12
17
|
function walk(dir, routePath, parentLayout, parentHead) {
|
|
13
18
|
const files = fs.readdirSync(dir);
|
|
14
19
|
const importPath = path.relative(viteRoot, dir).replaceAll("\\", "/");
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
|
|
20
|
+
const layoutFile = findFile(files, "+Layout", pageExtensionsX);
|
|
21
|
+
const currentLayout = layoutFile ? `${importPath}/${layoutFile}` : parentLayout;
|
|
22
|
+
const headFile = findFile(files, "+Head", pageExtensionsX);
|
|
23
|
+
const currentHead = headFile ? `${importPath}/${headFile}` : parentHead;
|
|
24
|
+
const pageFile = findFile(files, "+Page", pageExtensionsX);
|
|
25
|
+
if (pageFile) {
|
|
26
|
+
const dataFile = findFile(files, "+data", pageExtensions);
|
|
27
|
+
const titleFile = findFile(files, "+title", pageExtensions);
|
|
18
28
|
const route = {
|
|
19
29
|
path: routePath || "/",
|
|
20
|
-
page: `${importPath}
|
|
21
|
-
hasData:
|
|
22
|
-
hasTitle:
|
|
30
|
+
page: `${importPath}/${pageFile}`,
|
|
31
|
+
hasData: dataFile !== void 0,
|
|
32
|
+
hasTitle: titleFile !== void 0
|
|
23
33
|
};
|
|
34
|
+
if (dataFile) route.data = `${importPath}/${dataFile}`;
|
|
35
|
+
if (titleFile) route.title = `${importPath}/${titleFile}`;
|
|
24
36
|
if (currentLayout) route.layout = currentLayout;
|
|
25
37
|
if (currentHead) route.head = currentHead;
|
|
26
38
|
routes.push(route);
|
|
@@ -29,9 +41,10 @@ function generateRoutes(viteRoot, pagesDir) {
|
|
|
29
41
|
const fullPath = path.join(dir, file);
|
|
30
42
|
if (!fs.statSync(fullPath).isDirectory()) continue;
|
|
31
43
|
if (file === "_error") {
|
|
32
|
-
|
|
44
|
+
const errorPageFile = findFile(fs.readdirSync(fullPath), "+Page", pageExtensionsX);
|
|
45
|
+
if (errorPageFile) errorRoute = {
|
|
33
46
|
path: "_error",
|
|
34
|
-
page: `${importPath}/_error
|
|
47
|
+
page: `${importPath}/_error/${errorPageFile}`,
|
|
35
48
|
layout: currentLayout,
|
|
36
49
|
head: currentHead
|
|
37
50
|
};
|
|
@@ -174,8 +187,8 @@ function routerPlugin({ pagesDir = "pages", serverEntry = "server/index", apiPre
|
|
|
174
187
|
if (r.layout) code += `layout:'${r.layout}',Layout:()=>import('/${r.layout}'),`;
|
|
175
188
|
code += `hasData:${r.hasData},hasTitle:${r.hasTitle},`;
|
|
176
189
|
if (isSSR) {
|
|
177
|
-
if (r.hasData) code += `data:()=>import('/${r.
|
|
178
|
-
if (r.hasTitle) code += `title:()=>import('/${r.
|
|
190
|
+
if (r.hasData) code += `data:()=>import('/${r.data}'),`;
|
|
191
|
+
if (r.hasTitle) code += `title:()=>import('/${r.title}'),`;
|
|
179
192
|
}
|
|
180
193
|
code += "},";
|
|
181
194
|
}
|
|
@@ -206,16 +219,15 @@ function routerPlugin({ pagesDir = "pages", serverEntry = "server/index", apiPre
|
|
|
206
219
|
}
|
|
207
220
|
setVikeState({ routes, errorRoute, config, manifest });`;
|
|
208
221
|
if (id === resolvedVirtualEntryServerId) {
|
|
209
|
-
const extensions = [
|
|
210
|
-
".ts",
|
|
211
|
-
".js",
|
|
212
|
-
".mjs"
|
|
213
|
-
];
|
|
214
222
|
let hasCustomServer = false;
|
|
215
223
|
let customServerPath = "";
|
|
216
224
|
if (serverEntry) {
|
|
217
225
|
const basePath = path.resolve(viteConfigRoot, serverEntry);
|
|
218
|
-
for (const ext of [
|
|
226
|
+
for (const ext of [
|
|
227
|
+
".ts",
|
|
228
|
+
".js",
|
|
229
|
+
".mjs"
|
|
230
|
+
]) if (fs.existsSync(basePath + ext)) {
|
|
219
231
|
hasCustomServer = true;
|
|
220
232
|
customServerPath = (basePath + ext).replaceAll("\\", "/");
|
|
221
233
|
break;
|
|
@@ -241,11 +253,11 @@ if (process.env.NODE_ENV === 'production') {
|
|
|
241
253
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
242
254
|
const clientDir = path.resolve(__dirname, '../client');
|
|
243
255
|
const MIME_TYPES = {
|
|
244
|
-
'.html': 'text/html',
|
|
245
|
-
'.js': 'text/javascript',
|
|
246
|
-
'.mjs': 'text/javascript',
|
|
247
|
-
'.css': 'text/css',
|
|
248
|
-
'.json': 'application/json',
|
|
256
|
+
'.html': 'text/html; charset=utf-8',
|
|
257
|
+
'.js': 'text/javascript; charset=utf-8',
|
|
258
|
+
'.mjs': 'text/javascript; charset=utf-8',
|
|
259
|
+
'.css': 'text/css; charset=utf-8',
|
|
260
|
+
'.json': 'application/json; charset=utf-8',
|
|
249
261
|
'.png': 'image/png',
|
|
250
262
|
'.jpg': 'image/jpeg',
|
|
251
263
|
'.jpeg': 'image/jpeg',
|
|
@@ -254,23 +266,30 @@ if (process.env.NODE_ENV === 'production') {
|
|
|
254
266
|
'.ico': 'image/x-icon',
|
|
255
267
|
'.webmanifest': 'application/manifest+json',
|
|
256
268
|
'.xml': 'application/xml',
|
|
257
|
-
'.txt': 'text/plain',
|
|
269
|
+
'.txt': 'text/plain; charset=utf-8',
|
|
258
270
|
'.woff': 'font/woff',
|
|
259
|
-
'.woff2': 'font/woff2'
|
|
271
|
+
'.woff2': 'font/woff2',
|
|
272
|
+
'.wasm': 'application/wasm'
|
|
260
273
|
};
|
|
274
|
+
const base = import.meta.env.BASE_URL;
|
|
261
275
|
const server = createServer(async (req, res) => {
|
|
262
276
|
try {
|
|
263
277
|
const urlObj = new URL(req.url || '/', 'http://' + (req.headers.host || 'localhost'));
|
|
264
278
|
const pathname = urlObj.pathname;
|
|
265
|
-
|
|
266
|
-
|
|
279
|
+
let fileUrl = pathname;
|
|
280
|
+
if (base !== '/' && pathname.startsWith(base)) fileUrl = '/' + pathname.slice(base.length);
|
|
281
|
+
if (fileUrl !== '/') {
|
|
282
|
+
const filePath = path.resolve(clientDir, '.' + fileUrl)
|
|
283
|
+
if (!filePath.startsWith(clientDir + path.sep)) {
|
|
284
|
+
res.statusCode = 403
|
|
285
|
+
res.end('Forbidden')
|
|
286
|
+
return
|
|
287
|
+
}
|
|
267
288
|
if (filePath.startsWith(clientDir) && fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
268
289
|
const ext = path.extname(filePath).toLowerCase();
|
|
269
290
|
const mimeType = MIME_TYPES[ext] || 'application/octet-stream';
|
|
270
291
|
res.setHeader('Content-Type', mimeType);
|
|
271
|
-
|
|
272
|
-
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
|
|
273
|
-
}
|
|
292
|
+
res.setHeader('Cache-Control', fileUrl.startsWith('/assets/') ? 'public, max-age=31536000, immutable' : 'public, max-age=0, must-revalidate')
|
|
274
293
|
fs.createReadStream(filePath).pipe(res);
|
|
275
294
|
return;
|
|
276
295
|
}
|
|
@@ -282,6 +301,10 @@ if (process.env.NODE_ENV === 'production') {
|
|
|
282
301
|
}
|
|
283
302
|
const { method } = req;
|
|
284
303
|
const init = { method, headers };
|
|
304
|
+
if (req.method === 'HEAD') {
|
|
305
|
+
res.end()
|
|
306
|
+
return
|
|
307
|
+
}
|
|
285
308
|
if (method !== 'GET' && method !== 'HEAD') {
|
|
286
309
|
init.body = Readable.toWeb(req);
|
|
287
310
|
init.duplex = 'half';
|
|
@@ -292,11 +315,8 @@ if (process.env.NODE_ENV === 'production') {
|
|
|
292
315
|
for (const [key, val] of response.headers) {
|
|
293
316
|
res.setHeader(key, val);
|
|
294
317
|
}
|
|
295
|
-
if (response.body)
|
|
296
|
-
|
|
297
|
-
} else {
|
|
298
|
-
res.end();
|
|
299
|
-
}
|
|
318
|
+
if (response.body) await pipeline(Readable.fromWeb(response.body), res);
|
|
319
|
+
else res.end();
|
|
300
320
|
} catch (e) {
|
|
301
321
|
console.error(e);
|
|
302
322
|
res.statusCode = 500;
|