@qwik.dev/router 2.0.0-beta.36 → 2.0.0-beta.38
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/lib/adapters/cloudflare-pages/vite/index.mjs +3 -5
- package/lib/adapters/shared/vite/index.d.ts +3 -4
- package/lib/adapters/shared/vite/index.mjs +167 -155
- package/lib/chunks/dev-preloaded-route-loader.mjs +3 -0
- package/lib/chunks/format.mjs +31 -0
- package/lib/chunks/fs.mjs +10 -8
- package/lib/chunks/head.qwik.mjs +1425 -0
- package/lib/chunks/http-error.qwik.mjs +2 -2
- package/lib/chunks/pathname.mjs +3 -32
- package/lib/chunks/request-body-limit.mjs +37 -0
- package/lib/chunks/request-path.mjs +60 -0
- package/lib/chunks/routes.mjs +47 -0
- package/lib/chunks/server-error.mjs +19 -0
- package/lib/chunks/system.mjs +8 -8
- package/lib/chunks/use-functions.qwik.mjs +3 -12
- package/lib/chunks/user-response.mjs +1875 -0
- package/lib/chunks/worker-thread.mjs +317 -0
- package/lib/index.d.ts +251 -44
- package/lib/index.qwik.mjs +734 -485
- package/lib/middleware/aws-lambda/index.mjs +2 -1
- package/lib/middleware/azure-swa/index.mjs +13 -9
- package/lib/middleware/cloudflare-pages/index.mjs +2 -2
- package/lib/middleware/deno/index.d.ts +2 -0
- package/lib/middleware/deno/index.mjs +2 -1
- package/lib/middleware/netlify-edge/index.mjs +2 -2
- package/lib/middleware/node/index.d.ts +9 -1
- package/lib/middleware/node/index.mjs +28 -3
- package/lib/middleware/request-handler/index.d.ts +65 -26
- package/lib/middleware/request-handler/index.mjs +38 -1679
- package/lib/middleware/vercel-edge/index.mjs +2 -2
- package/lib/ssg/index.d.ts +5 -13
- package/lib/ssg/index.mjs +14 -53
- package/lib/vite/index.d.ts +23 -0
- package/lib/vite/index.mjs +533 -122
- package/package.json +4 -3
- package/lib/chunks/deepFreeze.qwik.mjs +0 -18
- package/lib/chunks/error-handler.mjs +0 -57
- package/lib/chunks/not-found-wrapper.qwik.mjs +0 -25
- package/lib/chunks/redirect-handler.mjs +0 -6
- package/lib/chunks/routing.qwik.mjs +0 -821
- package/lib/chunks/worker-thread.qwik.mjs +0 -2585
package/lib/vite/index.mjs
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { mergeConfig, normalizePath as normalizePath$1, loadEnv } from 'vite';
|
|
2
2
|
import fs, { existsSync } from 'node:fs';
|
|
3
|
+
import { readFile } from 'node:fs/promises';
|
|
3
4
|
import path, { join, dirname, basename, resolve, isAbsolute, extname } from 'node:path';
|
|
4
|
-
import {
|
|
5
|
-
import { i as
|
|
5
|
+
import { findExports, findStaticImports } from 'mlly';
|
|
6
|
+
import { g as getExtension, r as removeExtension, i as isIndexModule, a as isErrorName, b as isPageModuleExt, c as isModuleExt, d as isMarkdownExt, e as isLayoutModule, f as isEntryName, h as isMenuFileName, j as isServiceWorkerName, k as normalizePath, l as getPathnameFromDirPath, m as getMenuPathname, o as createFileId, p as parseRouteIndexName, q as errorBoundaryName, s as isPluginModule, t as isPageExt } from '../chunks/fs.mjs';
|
|
7
|
+
import { a as addError, b as addWarning } from '../chunks/format.mjs';
|
|
8
|
+
import { i as isSameOriginUrl, e as ensureSlash } from '../chunks/pathname.mjs';
|
|
9
|
+
import { c as createRouteTester } from '../chunks/routes.mjs';
|
|
6
10
|
import { marked } from 'marked';
|
|
7
11
|
import { createHash } from 'node:crypto';
|
|
8
12
|
import { SourceMapGenerator } from 'source-map';
|
|
@@ -15,6 +19,8 @@ import { toString } from 'hast-util-to-string';
|
|
|
15
19
|
import { refractor } from 'refractor';
|
|
16
20
|
import tsxLang from 'refractor/lang/tsx.js';
|
|
17
21
|
import { optimize } from 'svgo';
|
|
22
|
+
import { r as recognizeRequest, I as IsQLoader, t as trimRecognizedInternalPathname, a as resolveValidInternalFullPathname } from '../chunks/request-path.mjs';
|
|
23
|
+
import { d as devPreloadedRouteLoaders } from '../chunks/dev-preloaded-route-loader.mjs';
|
|
18
24
|
|
|
19
25
|
function extendConfig(baseConfigExport, serverConfigExport) {
|
|
20
26
|
return async (env) => {
|
|
@@ -96,7 +102,7 @@ The link "${url}", found within "${containingFilePath}" does not have a matching
|
|
|
96
102
|
url = url.slice(0, -1);
|
|
97
103
|
}
|
|
98
104
|
} else if (!globalThis.__NO_TRAILING_SLASH__) {
|
|
99
|
-
url
|
|
105
|
+
url = ensureSlash(url);
|
|
100
106
|
}
|
|
101
107
|
}
|
|
102
108
|
return url;
|
|
@@ -262,43 +268,6 @@ function parseRoutePathname(basePathname, pathname) {
|
|
|
262
268
|
};
|
|
263
269
|
}
|
|
264
270
|
|
|
265
|
-
function routeSortCompare(a, b) {
|
|
266
|
-
const maxSegments = Math.max(a.segments.length, b.segments.length);
|
|
267
|
-
for (let i = 0; i < maxSegments; i += 1) {
|
|
268
|
-
const sa = a.segments[i];
|
|
269
|
-
const sb = b.segments[i];
|
|
270
|
-
if (!sa) {
|
|
271
|
-
return a.pathname.includes("[...") ? 1 : -1;
|
|
272
|
-
}
|
|
273
|
-
if (!sb) {
|
|
274
|
-
return b.pathname.includes("[...") ? -1 : 1;
|
|
275
|
-
}
|
|
276
|
-
const maxParts = Math.max(sa.length, sb.length);
|
|
277
|
-
for (let i2 = 0; i2 < maxParts; i2 += 1) {
|
|
278
|
-
const pa = sa[i2];
|
|
279
|
-
const pb = sb[i2];
|
|
280
|
-
if (pa === void 0) {
|
|
281
|
-
return pb.dynamic ? -1 : 1;
|
|
282
|
-
}
|
|
283
|
-
if (pb === void 0) {
|
|
284
|
-
return pa.dynamic ? 1 : -1;
|
|
285
|
-
}
|
|
286
|
-
if (pa.dynamic !== pb.dynamic) {
|
|
287
|
-
return pa.dynamic ? 1 : -1;
|
|
288
|
-
}
|
|
289
|
-
if (pa.dynamic) {
|
|
290
|
-
if (pa.rest !== pb.rest) {
|
|
291
|
-
return pa.rest ? 1 : -1;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
if (a.pathname === b.pathname) {
|
|
297
|
-
return a.ext > b.ext ? -1 : 1;
|
|
298
|
-
}
|
|
299
|
-
return a.pathname < b.pathname ? -1 : 1;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
271
|
const LAYOUT_ID = "layout";
|
|
303
272
|
const LAYOUT_NAMED_PREFIX = LAYOUT_ID + "-";
|
|
304
273
|
const LAYOUT_TOP_SUFFIX = "!";
|
|
@@ -334,8 +303,9 @@ function resolveRoute(opts, appLayouts, sourceFile) {
|
|
|
334
303
|
const routesDir = opts.routesDir;
|
|
335
304
|
const { layoutName, layoutStop } = parseRouteIndexName(sourceFile.extlessName);
|
|
336
305
|
let pathname = getPathnameFromDirPath(opts, sourceFile.dirPath);
|
|
337
|
-
|
|
338
|
-
|
|
306
|
+
const boundary = errorBoundaryName(sourceFile.extlessName);
|
|
307
|
+
if (boundary) {
|
|
308
|
+
pathname += boundary + ".html";
|
|
339
309
|
}
|
|
340
310
|
if (!layoutStop) {
|
|
341
311
|
let currentDir = normalizePath(dirname(filePath));
|
|
@@ -373,6 +343,43 @@ function resolveRoute(opts, appLayouts, sourceFile) {
|
|
|
373
343
|
};
|
|
374
344
|
}
|
|
375
345
|
|
|
346
|
+
function routeSortCompare(a, b) {
|
|
347
|
+
const maxSegments = Math.max(a.segments.length, b.segments.length);
|
|
348
|
+
for (let i = 0; i < maxSegments; i += 1) {
|
|
349
|
+
const sa = a.segments[i];
|
|
350
|
+
const sb = b.segments[i];
|
|
351
|
+
if (!sa) {
|
|
352
|
+
return a.pathname.includes("[...") ? 1 : -1;
|
|
353
|
+
}
|
|
354
|
+
if (!sb) {
|
|
355
|
+
return b.pathname.includes("[...") ? -1 : 1;
|
|
356
|
+
}
|
|
357
|
+
const maxParts = Math.max(sa.length, sb.length);
|
|
358
|
+
for (let i2 = 0; i2 < maxParts; i2 += 1) {
|
|
359
|
+
const pa = sa[i2];
|
|
360
|
+
const pb = sb[i2];
|
|
361
|
+
if (pa === void 0) {
|
|
362
|
+
return pb.dynamic ? -1 : 1;
|
|
363
|
+
}
|
|
364
|
+
if (pb === void 0) {
|
|
365
|
+
return pa.dynamic ? 1 : -1;
|
|
366
|
+
}
|
|
367
|
+
if (pa.dynamic !== pb.dynamic) {
|
|
368
|
+
return pa.dynamic ? 1 : -1;
|
|
369
|
+
}
|
|
370
|
+
if (pa.dynamic) {
|
|
371
|
+
if (pa.rest !== pb.rest) {
|
|
372
|
+
return pa.rest ? 1 : -1;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
if (a.pathname === b.pathname) {
|
|
378
|
+
return a.ext > b.ext ? -1 : 1;
|
|
379
|
+
}
|
|
380
|
+
return a.pathname < b.pathname ? -1 : 1;
|
|
381
|
+
}
|
|
382
|
+
|
|
376
383
|
function parseDirName(name) {
|
|
377
384
|
if (name.startsWith("(") && name.endsWith(")")) {
|
|
378
385
|
return {
|
|
@@ -411,7 +418,6 @@ async function walkRouteDir(node, dirPath) {
|
|
|
411
418
|
if (!child) {
|
|
412
419
|
child = {
|
|
413
420
|
_files: [],
|
|
414
|
-
_dirPath: itemPath,
|
|
415
421
|
children: /* @__PURE__ */ new Map()
|
|
416
422
|
};
|
|
417
423
|
node.children.set(itemName, child);
|
|
@@ -422,7 +428,6 @@ async function walkRouteDir(node, dirPath) {
|
|
|
422
428
|
if (!child) {
|
|
423
429
|
child = {
|
|
424
430
|
_files: [],
|
|
425
|
-
_dirPath: itemPath,
|
|
426
431
|
children: /* @__PURE__ */ new Map()
|
|
427
432
|
};
|
|
428
433
|
if (parsed.paramName) {
|
|
@@ -456,7 +461,6 @@ async function walkRoutes(routesDir) {
|
|
|
456
461
|
const dirPath = normalizePath(routesDir);
|
|
457
462
|
const root = {
|
|
458
463
|
_files: [],
|
|
459
|
-
_dirPath: dirPath,
|
|
460
464
|
children: /* @__PURE__ */ new Map()
|
|
461
465
|
};
|
|
462
466
|
await walkRouteDir(root, dirPath);
|
|
@@ -569,7 +573,7 @@ function deriveFromTrie(opts, root) {
|
|
|
569
573
|
function translateRoute(route, config, configIndex) {
|
|
570
574
|
const replacePath = (part) => (config.paths || {})[part] ?? part;
|
|
571
575
|
const pathnamePrefix = config.prefix ? "/" + config.prefix : "";
|
|
572
|
-
const routeNamePrefix = config.prefix ? config.prefix
|
|
576
|
+
const routeNamePrefix = config.prefix ? ensureSlash(config.prefix) : "";
|
|
573
577
|
const idSuffix = config.prefix?.toUpperCase().replace(/-/g, "");
|
|
574
578
|
const patternInfix = config.prefix ? [
|
|
575
579
|
config.prefix
|
|
@@ -641,7 +645,7 @@ function rewriteRoutes(ctx, routes) {
|
|
|
641
645
|
function applyRewriteRoutes(root, rewriteConfigs) {
|
|
642
646
|
const routables = [];
|
|
643
647
|
function walk(node, steps) {
|
|
644
|
-
const hasRoute = node._files.some((f) => f.type === "route" && f.extlessName
|
|
648
|
+
const hasRoute = node._files.some((f) => f.type === "route" && !isErrorName(f.extlessName));
|
|
645
649
|
if (hasRoute) {
|
|
646
650
|
routables.push({
|
|
647
651
|
steps: [
|
|
@@ -671,7 +675,12 @@ function applyRewriteRoutes(root, rewriteConfigs) {
|
|
|
671
675
|
for (const config of rewriteConfigs) {
|
|
672
676
|
const translations = config.paths || {};
|
|
673
677
|
const translatable = new Set(Object.keys(translations).map((k) => k.toLowerCase()));
|
|
678
|
+
const isExcluded = config.exclude?.length ? createRouteTester("/", config.exclude, void 0) : void 0;
|
|
674
679
|
for (const { steps } of routables) {
|
|
680
|
+
const originalKeyPath = steps.map((s) => s.key).join("/");
|
|
681
|
+
if (isExcluded?.("/" + originalKeyPath)) {
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
675
684
|
const hasTranslatable = steps.some((s) => translatable.has(s.key));
|
|
676
685
|
if (!hasTranslatable && !config.prefix) {
|
|
677
686
|
continue;
|
|
@@ -679,7 +688,6 @@ function applyRewriteRoutes(root, rewriteConfigs) {
|
|
|
679
688
|
if (steps.length === 0 && !config.prefix) {
|
|
680
689
|
continue;
|
|
681
690
|
}
|
|
682
|
-
const originalKeyPath = steps.map((s) => s.key).join("/");
|
|
683
691
|
const translatedSteps = [];
|
|
684
692
|
if (config.prefix) {
|
|
685
693
|
translatedSteps.push({
|
|
@@ -703,7 +711,6 @@ function applyRewriteRoutes(root, rewriteConfigs) {
|
|
|
703
711
|
if (!child) {
|
|
704
712
|
child = {
|
|
705
713
|
_files: [],
|
|
706
|
-
_dirPath: "",
|
|
707
714
|
children: /* @__PURE__ */ new Map()
|
|
708
715
|
};
|
|
709
716
|
if (step.paramName) {
|
|
@@ -788,7 +795,7 @@ function normalizeOptions(rootDir, viteBasePath, userOpts) {
|
|
|
788
795
|
if (!(viteBasePath.startsWith("/") && viteBasePath.endsWith("/"))) {
|
|
789
796
|
console.error(`warning: vite's config.base must begin and end with /. This will be an error in v2. If you have a valid use case, please open an issue.`);
|
|
790
797
|
if (!viteBasePath.endsWith("/")) {
|
|
791
|
-
viteBasePath
|
|
798
|
+
viteBasePath = ensureSlash(viteBasePath);
|
|
792
799
|
}
|
|
793
800
|
}
|
|
794
801
|
const opts = {
|
|
@@ -814,12 +821,15 @@ function normalizeOptions(rootDir, viteBasePath, userOpts) {
|
|
|
814
821
|
}
|
|
815
822
|
if (!opts.basePathname.endsWith("/")) {
|
|
816
823
|
console.error(`Warning: qwik-router plugin basePathname must end with /. This will be an error in v2`);
|
|
817
|
-
opts.basePathname
|
|
824
|
+
opts.basePathname = ensureSlash(opts.basePathname);
|
|
818
825
|
}
|
|
819
826
|
const url = new URL(opts.basePathname, "https://qwik.dev/");
|
|
820
827
|
opts.basePathname = url.pathname;
|
|
821
828
|
opts.mdx = opts.mdx || {};
|
|
822
829
|
opts.platform = opts.platform || {};
|
|
830
|
+
if (opts.strictLoaders === void 0) {
|
|
831
|
+
opts.strictLoaders = true;
|
|
832
|
+
}
|
|
823
833
|
return opts;
|
|
824
834
|
}
|
|
825
835
|
function createBuildContext(rootDir, viteBasePath, userOpts, target, dynamicImports) {
|
|
@@ -828,7 +838,6 @@ function createBuildContext(rootDir, viteBasePath, userOpts, target, dynamicImpo
|
|
|
828
838
|
opts: normalizeOptions(rootDir, viteBasePath, userOpts),
|
|
829
839
|
routeTrie: {
|
|
830
840
|
_files: [],
|
|
831
|
-
_dirPath: "",
|
|
832
841
|
children: /* @__PURE__ */ new Map()
|
|
833
842
|
},
|
|
834
843
|
routes: [],
|
|
@@ -1286,6 +1295,20 @@ function resolveNamedLayoutChain(ancestorLayouts, nodeLayouts, targetName) {
|
|
|
1286
1295
|
}
|
|
1287
1296
|
return result;
|
|
1288
1297
|
}
|
|
1298
|
+
function buildLoaderChainExpr(extlessName, loaderExpr, ancestorLayouts, nodeLayouts) {
|
|
1299
|
+
const { layoutName, layoutStop } = parseRouteIndexName(extlessName);
|
|
1300
|
+
if (layoutStop) {
|
|
1301
|
+
return `[ ${loaderExpr} ]`;
|
|
1302
|
+
}
|
|
1303
|
+
if (layoutName) {
|
|
1304
|
+
const chain = resolveNamedLayoutChain(ancestorLayouts, nodeLayouts, layoutName);
|
|
1305
|
+
return `[ ${[
|
|
1306
|
+
...chain.map((l) => l.id),
|
|
1307
|
+
loaderExpr
|
|
1308
|
+
].join(", ")} ]`;
|
|
1309
|
+
}
|
|
1310
|
+
return loaderExpr;
|
|
1311
|
+
}
|
|
1289
1312
|
function getClientRouteBundleNames(qwikPlugin, r) {
|
|
1290
1313
|
const bundlesNames = [];
|
|
1291
1314
|
const manifest = qwikPlugin.api.getManifest();
|
|
@@ -1314,7 +1337,7 @@ function getClientRouteBundleNames(qwikPlugin, r) {
|
|
|
1314
1337
|
}
|
|
1315
1338
|
return bundlesNames;
|
|
1316
1339
|
}
|
|
1317
|
-
function serializeBuildTrie(ctx, qwikPlugin, node, layoutIdMap, routeIdMap, menuIdMap,
|
|
1340
|
+
function serializeBuildTrie(ctx, qwikPlugin, node, layoutIdMap, routeIdMap, menuIdMap, ancestorLayouts, isSSR, indent, loadersByFile, routeLoaderSourceFiles) {
|
|
1318
1341
|
const lines = [];
|
|
1319
1342
|
const nextIndent = indent + " ";
|
|
1320
1343
|
if (node._P) {
|
|
@@ -1331,7 +1354,6 @@ function serializeBuildTrie(ctx, qwikPlugin, node, layoutIdMap, routeIdMap, menu
|
|
|
1331
1354
|
}
|
|
1332
1355
|
let layoutExpr;
|
|
1333
1356
|
let indexExpr;
|
|
1334
|
-
let indexIsOverride = false;
|
|
1335
1357
|
let errorExpr;
|
|
1336
1358
|
let notFoundExpr;
|
|
1337
1359
|
let menuExpr;
|
|
@@ -1372,28 +1394,14 @@ function serializeBuildTrie(ctx, qwikPlugin, node, layoutIdMap, routeIdMap, menu
|
|
|
1372
1394
|
if (!loaderExpr) {
|
|
1373
1395
|
continue;
|
|
1374
1396
|
}
|
|
1375
|
-
const
|
|
1376
|
-
const
|
|
1377
|
-
if (
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
notFoundExpr = loaderExpr;
|
|
1382
|
-
notFoundFiles.set(node._dirPath, file.filePath);
|
|
1397
|
+
const expr = buildLoaderChainExpr(file.extlessName, loaderExpr, ancestorLayouts, nodeLayouts);
|
|
1398
|
+
const boundary = errorBoundaryName(file.extlessName);
|
|
1399
|
+
if (boundary === "404") {
|
|
1400
|
+
notFoundExpr = expr;
|
|
1401
|
+
} else if (boundary === "error") {
|
|
1402
|
+
errorExpr = expr;
|
|
1383
1403
|
} else {
|
|
1384
|
-
|
|
1385
|
-
if (layoutStop) {
|
|
1386
|
-
indexExpr = `[ ${loaderExpr} ]`;
|
|
1387
|
-
indexIsOverride = true;
|
|
1388
|
-
} else if (layoutName) {
|
|
1389
|
-
const chain = resolveNamedLayoutChain(ancestorLayouts, nodeLayouts, layoutName);
|
|
1390
|
-
const chainExprs = chain.map((l) => l.id);
|
|
1391
|
-
chainExprs.push(loaderExpr);
|
|
1392
|
-
indexExpr = `[ ${chainExprs.join(", ")} ]`;
|
|
1393
|
-
indexIsOverride = true;
|
|
1394
|
-
} else {
|
|
1395
|
-
indexExpr = loaderExpr;
|
|
1396
|
-
}
|
|
1404
|
+
indexExpr = expr;
|
|
1397
1405
|
bundleRoute = ctx.routes.find((r) => r.filePath === file.filePath);
|
|
1398
1406
|
}
|
|
1399
1407
|
}
|
|
@@ -1401,11 +1409,7 @@ function serializeBuildTrie(ctx, qwikPlugin, node, layoutIdMap, routeIdMap, menu
|
|
|
1401
1409
|
lines.push(`${nextIndent}_L: ${layoutExpr},`);
|
|
1402
1410
|
}
|
|
1403
1411
|
if (indexExpr) {
|
|
1404
|
-
|
|
1405
|
-
lines.push(`${nextIndent}_I: ${indexExpr},`);
|
|
1406
|
-
} else {
|
|
1407
|
-
lines.push(`${nextIndent}_I: ${indexExpr},`);
|
|
1408
|
-
}
|
|
1412
|
+
lines.push(`${nextIndent}_I: ${indexExpr},`);
|
|
1409
1413
|
if (isSSR && bundleRoute) {
|
|
1410
1414
|
const bundleNames = getClientRouteBundleNames(qwikPlugin, bundleRoute);
|
|
1411
1415
|
if (bundleNames.length > 0) {
|
|
@@ -1413,6 +1417,37 @@ function serializeBuildTrie(ctx, qwikPlugin, node, layoutIdMap, routeIdMap, menu
|
|
|
1413
1417
|
}
|
|
1414
1418
|
}
|
|
1415
1419
|
}
|
|
1420
|
+
{
|
|
1421
|
+
const routeFiles = [];
|
|
1422
|
+
for (const file of node._files.filter((f) => f.type === "layout" || f.type === "route" && routeIdMap.has(f.filePath))) {
|
|
1423
|
+
routeFiles.push(file.filePath, ...routeLoaderSourceFiles?.get(file.filePath) ?? []);
|
|
1424
|
+
}
|
|
1425
|
+
if (node === ctx.routeTrie) {
|
|
1426
|
+
for (const plugin of ctx.serverPlugins) {
|
|
1427
|
+
routeFiles.push(plugin.filePath, ...routeLoaderSourceFiles?.get(plugin.filePath) ?? []);
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
const routeLoaderFiles = [
|
|
1431
|
+
...new Set(routeFiles)
|
|
1432
|
+
];
|
|
1433
|
+
if (routeLoaderFiles.length > 0) {
|
|
1434
|
+
if (loadersByFile) {
|
|
1435
|
+
const nodeLoaderHashes = [];
|
|
1436
|
+
for (const filePath of routeLoaderFiles) {
|
|
1437
|
+
const hashes = loadersByFile.get(filePath);
|
|
1438
|
+
if (hashes) {
|
|
1439
|
+
nodeLoaderHashes.push(...hashes);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
if (nodeLoaderHashes.length > 0) {
|
|
1443
|
+
lines.push(`${nextIndent}_R: ${JSON.stringify(nodeLoaderHashes)},`);
|
|
1444
|
+
}
|
|
1445
|
+
} else {
|
|
1446
|
+
const placeholder = `__LOADERS:${routeLoaderFiles.join("|")}__`;
|
|
1447
|
+
lines.push(`${nextIndent}_R: ${JSON.stringify(placeholder)},`);
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1416
1451
|
if (errorExpr) {
|
|
1417
1452
|
lines.push(`${nextIndent}_E: ${errorExpr},`);
|
|
1418
1453
|
}
|
|
@@ -1445,7 +1480,7 @@ function serializeBuildTrie(ctx, qwikPlugin, node, layoutIdMap, routeIdMap, menu
|
|
|
1445
1480
|
groupChildren.sort((a, b) => a[0].localeCompare(b[0]));
|
|
1446
1481
|
const groupStrs = [];
|
|
1447
1482
|
for (const [_key, child] of groupChildren) {
|
|
1448
|
-
const childStr = serializeBuildTrie(ctx, qwikPlugin, child, layoutIdMap, routeIdMap, menuIdMap,
|
|
1483
|
+
const childStr = serializeBuildTrie(ctx, qwikPlugin, child, layoutIdMap, routeIdMap, menuIdMap, childAncestors, isSSR, nextIndent, loadersByFile, routeLoaderSourceFiles);
|
|
1449
1484
|
if (childStr !== "{}") {
|
|
1450
1485
|
groupStrs.push(childStr);
|
|
1451
1486
|
}
|
|
@@ -1455,7 +1490,7 @@ function serializeBuildTrie(ctx, qwikPlugin, node, layoutIdMap, routeIdMap, menu
|
|
|
1455
1490
|
}
|
|
1456
1491
|
}
|
|
1457
1492
|
for (const [key, child] of regularChildren) {
|
|
1458
|
-
const childStr = serializeBuildTrie(ctx, qwikPlugin, child, layoutIdMap, routeIdMap, menuIdMap,
|
|
1493
|
+
const childStr = serializeBuildTrie(ctx, qwikPlugin, child, layoutIdMap, routeIdMap, menuIdMap, childAncestors, isSSR, nextIndent, loadersByFile, routeLoaderSourceFiles);
|
|
1459
1494
|
if (childStr !== "{}") {
|
|
1460
1495
|
const keyStr = JSON.stringify(key);
|
|
1461
1496
|
lines.push(`${nextIndent}${keyStr}: ${childStr},`);
|
|
@@ -1468,14 +1503,12 @@ function serializeBuildTrie(ctx, qwikPlugin, node, layoutIdMap, routeIdMap, menu
|
|
|
1468
1503
|
${lines.join("\n")}
|
|
1469
1504
|
${indent}}`;
|
|
1470
1505
|
}
|
|
1471
|
-
function createRoutes(ctx, qwikPlugin, c, esmImports, isSSR) {
|
|
1506
|
+
function createRoutes(ctx, qwikPlugin, c, esmImports, isSSR, loadersByFile, serverExcludePaths, routeLoaderSourceFiles) {
|
|
1472
1507
|
const includeEndpoints = isSSR;
|
|
1473
1508
|
const dynamicImports = ctx.dynamicImports;
|
|
1474
1509
|
const layoutIdMap = /* @__PURE__ */ new Map();
|
|
1475
1510
|
const routeIdMap = /* @__PURE__ */ new Map();
|
|
1476
1511
|
const menuIdMap = /* @__PURE__ */ new Map();
|
|
1477
|
-
const errorFiles = /* @__PURE__ */ new Map();
|
|
1478
|
-
const notFoundFiles = /* @__PURE__ */ new Map();
|
|
1479
1512
|
let layoutCount = 0;
|
|
1480
1513
|
let routeCount = 0;
|
|
1481
1514
|
let menuCount = 0;
|
|
@@ -1527,6 +1560,9 @@ function createRoutes(ctx, qwikPlugin, c, esmImports, isSSR) {
|
|
|
1527
1560
|
/** Qwik Router Routes (${routeCount}) */`);
|
|
1528
1561
|
for (const route of ctx.routes) {
|
|
1529
1562
|
if (isPageExt(route.ext)) {
|
|
1563
|
+
if (serverExcludePaths?.has(route.filePath)) {
|
|
1564
|
+
continue;
|
|
1565
|
+
}
|
|
1530
1566
|
const importPath = getImportPath(route.filePath);
|
|
1531
1567
|
let loaderExpr;
|
|
1532
1568
|
if (dynamicImports) {
|
|
@@ -1542,7 +1578,7 @@ function createRoutes(ctx, qwikPlugin, c, esmImports, isSSR) {
|
|
|
1542
1578
|
routeIdMap.set(route.filePath, `()=>${route.id}`);
|
|
1543
1579
|
}
|
|
1544
1580
|
}
|
|
1545
|
-
const trieStr = serializeBuildTrie(ctx, qwikPlugin, ctx.routeTrie, layoutIdMap, routeIdMap, menuIdMap,
|
|
1581
|
+
const trieStr = serializeBuildTrie(ctx, qwikPlugin, ctx.routeTrie, layoutIdMap, routeIdMap, menuIdMap, [], isSSR, "", loadersByFile, routeLoaderSourceFiles);
|
|
1546
1582
|
const baseSegments = ctx.opts.basePathname.split("/").filter((s) => s.length > 0);
|
|
1547
1583
|
let routesExpr = trieStr;
|
|
1548
1584
|
for (let j = baseSegments.length - 1; j >= 0; j--) {
|
|
@@ -1567,7 +1603,7 @@ function createServerPlugins(ctx, _qwikPlugin, c, esmImports, isSSR) {
|
|
|
1567
1603
|
c.push(`];`);
|
|
1568
1604
|
}
|
|
1569
1605
|
|
|
1570
|
-
function generateQwikRouterConfig(ctx, qwikPlugin, isSSR) {
|
|
1606
|
+
function generateQwikRouterConfig(ctx, qwikPlugin, isSSR, loadersByFile, serverExcludePaths, routeLoaderSourceFiles) {
|
|
1571
1607
|
const esmImports = [];
|
|
1572
1608
|
const c = [];
|
|
1573
1609
|
c.push(`
|
|
@@ -1578,7 +1614,7 @@ import { isDev } from '@qwik.dev/core/build';`);
|
|
|
1578
1614
|
esmImports.push(`import 'virtual:qwik-router-server-fns';`);
|
|
1579
1615
|
}
|
|
1580
1616
|
createServerPlugins(ctx, qwikPlugin, c, esmImports, isSSR);
|
|
1581
|
-
createRoutes(ctx, qwikPlugin, c, esmImports, isSSR);
|
|
1617
|
+
createRoutes(ctx, qwikPlugin, c, esmImports, isSSR, loadersByFile, serverExcludePaths, routeLoaderSourceFiles);
|
|
1582
1618
|
createEntries(ctx, c);
|
|
1583
1619
|
c.push(`export const trailingSlash = ${JSON.stringify(!globalThis.__NO_TRAILING_SLASH__)};`);
|
|
1584
1620
|
c.push(`export const basePathname = ${JSON.stringify(ctx.opts.basePathname)};`);
|
|
@@ -1604,6 +1640,76 @@ function generateServiceWorkerRegister(ctx, swRegister) {
|
|
|
1604
1640
|
return `export default ${JSON.stringify(swReg)};`;
|
|
1605
1641
|
}
|
|
1606
1642
|
|
|
1643
|
+
const NON_GET_HANDLERS = /* @__PURE__ */ new Set([
|
|
1644
|
+
"onRequest",
|
|
1645
|
+
"onPost",
|
|
1646
|
+
"onPut",
|
|
1647
|
+
"onPatch",
|
|
1648
|
+
"onDelete",
|
|
1649
|
+
"onHead",
|
|
1650
|
+
"onOptions"
|
|
1651
|
+
]);
|
|
1652
|
+
const ROUTER_MODULE = "@qwik.dev/router";
|
|
1653
|
+
const SERVER_QRL = /\b(?:routeLoader|routeAction|globalAction|server)(?:\$|Qrl)/;
|
|
1654
|
+
function isServerFreeSource(code) {
|
|
1655
|
+
for (const e of findExports(code)) {
|
|
1656
|
+
if (e.specifier) {
|
|
1657
|
+
return false;
|
|
1658
|
+
}
|
|
1659
|
+
const names = [
|
|
1660
|
+
...e.name ? [
|
|
1661
|
+
e.name
|
|
1662
|
+
] : [],
|
|
1663
|
+
...e.names ?? []
|
|
1664
|
+
];
|
|
1665
|
+
if (names.some((n) => NON_GET_HANDLERS.has(n))) {
|
|
1666
|
+
return false;
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
for (const imp of findStaticImports(code)) {
|
|
1670
|
+
if (imp.specifier === ROUTER_MODULE && SERVER_QRL.test(imp.code)) {
|
|
1671
|
+
return false;
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
return true;
|
|
1675
|
+
}
|
|
1676
|
+
function isFileServerFree(filePath, cache) {
|
|
1677
|
+
let pending = cache.get(filePath);
|
|
1678
|
+
if (!pending) {
|
|
1679
|
+
pending = readFile(filePath, "utf-8").then(isServerFreeSource).catch(() => false);
|
|
1680
|
+
cache.set(filePath, pending);
|
|
1681
|
+
}
|
|
1682
|
+
return pending;
|
|
1683
|
+
}
|
|
1684
|
+
async function isRouteServerFree(route, cache) {
|
|
1685
|
+
if (!isMarkdownExt(route.ext) && !await isFileServerFree(route.filePath, cache)) {
|
|
1686
|
+
return false;
|
|
1687
|
+
}
|
|
1688
|
+
for (const layout of route.layouts) {
|
|
1689
|
+
if (!await isFileServerFree(layout.filePath, cache)) {
|
|
1690
|
+
return false;
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
return true;
|
|
1694
|
+
}
|
|
1695
|
+
async function getServerExcludedRoutes(ctx, ssg) {
|
|
1696
|
+
const excluded = /* @__PURE__ */ new Set();
|
|
1697
|
+
if (!ssg?.include?.length) {
|
|
1698
|
+
return excluded;
|
|
1699
|
+
}
|
|
1700
|
+
const isPrerendered = createRouteTester(ctx.opts.basePathname, ssg.include, ssg.exclude);
|
|
1701
|
+
const serverFreeByFile = /* @__PURE__ */ new Map();
|
|
1702
|
+
await Promise.all(ctx.routes.map(async (route) => {
|
|
1703
|
+
if (!isPageExt(route.ext) || route.paramNames.length > 0 || /\/(?:404|error)\.html$/.test(route.pathname) || !isPrerendered(route.pathname)) {
|
|
1704
|
+
return;
|
|
1705
|
+
}
|
|
1706
|
+
if (await isRouteServerFree(route, serverFreeByFile)) {
|
|
1707
|
+
excluded.add(route.filePath);
|
|
1708
|
+
}
|
|
1709
|
+
}));
|
|
1710
|
+
return excluded;
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1607
1713
|
function isBundlePartOfRoute(bundle, routeAndLayoutPaths) {
|
|
1608
1714
|
if (!bundle.origins) {
|
|
1609
1715
|
return false;
|
|
@@ -1777,8 +1883,22 @@ function createVirtualImageJsxId(pathId, params) {
|
|
|
1777
1883
|
const queryString = query.toString();
|
|
1778
1884
|
return `${VIRTUAL_IMAGE_JSX_PREFIX}${normalizePath$1(pathId)}${VIRTUAL_IMAGE_JSX_SUFFIX}${queryString ? `?${queryString}` : ""}`;
|
|
1779
1885
|
}
|
|
1780
|
-
function
|
|
1781
|
-
const
|
|
1886
|
+
function createImageJsxDirectives(params, userOpts) {
|
|
1887
|
+
const overrides = Object.fromEntries(params.entries());
|
|
1888
|
+
delete overrides[JSX_QUERY_PARAM];
|
|
1889
|
+
delete overrides[INTERNAL_IMAGE_JSX_QUERY_PARAM];
|
|
1890
|
+
return new URLSearchParams({
|
|
1891
|
+
format: "webp",
|
|
1892
|
+
quality: "75",
|
|
1893
|
+
w: "200;400;600;800;1200",
|
|
1894
|
+
withoutEnlargement: "",
|
|
1895
|
+
...userOpts?.imageOptimization?.jsxDirectives,
|
|
1896
|
+
...overrides,
|
|
1897
|
+
as: "jsx"
|
|
1898
|
+
});
|
|
1899
|
+
}
|
|
1900
|
+
function createImageJsxImportId(pathId, params, userOpts) {
|
|
1901
|
+
const query = createImageJsxDirectives(params, userOpts);
|
|
1782
1902
|
query.set(INTERNAL_IMAGE_JSX_QUERY_PARAM, "");
|
|
1783
1903
|
return `${normalizePath$1(pathId)}?${query.toString()}`;
|
|
1784
1904
|
}
|
|
@@ -1806,9 +1926,10 @@ function getToImg() {
|
|
|
1806
1926
|
: _jsxSplit("img", null, {decoding, height: h, loading, srcSet: s, width: w});
|
|
1807
1927
|
`;
|
|
1808
1928
|
}
|
|
1809
|
-
function createImageJsxModule(pathId, params) {
|
|
1929
|
+
function createImageJsxModule(pathId, params, userOpts) {
|
|
1930
|
+
const importId = createImageJsxImportId(pathId, params, userOpts);
|
|
1810
1931
|
return `
|
|
1811
|
-
import { srcSet, width, height } from ${JSON.stringify(
|
|
1932
|
+
import { srcSet, width, height } from ${JSON.stringify(importId)};
|
|
1812
1933
|
import toImg from ${JSON.stringify(TO_IMG_ID)};
|
|
1813
1934
|
|
|
1814
1935
|
export default toImg(srcSet, width, height);
|
|
@@ -1906,18 +2027,7 @@ function imagePlugin(userOpts) {
|
|
|
1906
2027
|
},
|
|
1907
2028
|
defaultDirectives: (url) => {
|
|
1908
2029
|
if (url.searchParams.has(JSX_QUERY_PARAM)) {
|
|
1909
|
-
|
|
1910
|
-
delete params[JSX_QUERY_PARAM];
|
|
1911
|
-
delete params[INTERNAL_IMAGE_JSX_QUERY_PARAM];
|
|
1912
|
-
return new URLSearchParams({
|
|
1913
|
-
format: "webp",
|
|
1914
|
-
quality: "75",
|
|
1915
|
-
w: "200;400;600;800;1200",
|
|
1916
|
-
withoutEnlargement: "",
|
|
1917
|
-
...userOpts?.imageOptimization?.jsxDirectives,
|
|
1918
|
-
...params,
|
|
1919
|
-
as: "jsx"
|
|
1920
|
-
});
|
|
2030
|
+
return createImageJsxDirectives(url.searchParams, userOpts);
|
|
1921
2031
|
}
|
|
1922
2032
|
return new URLSearchParams();
|
|
1923
2033
|
}
|
|
@@ -1999,7 +2109,7 @@ function imagePlugin(userOpts) {
|
|
|
1999
2109
|
}
|
|
2000
2110
|
if (supportedExtensions.includes(imageId.extension)) {
|
|
2001
2111
|
return {
|
|
2002
|
-
code: createImageJsxModule(imageId.pathId, imageId.params),
|
|
2112
|
+
code: createImageJsxModule(imageId.pathId, imageId.params, userOpts),
|
|
2003
2113
|
map: null
|
|
2004
2114
|
};
|
|
2005
2115
|
}
|
|
@@ -2045,15 +2155,21 @@ async function collectServerFnModuleIds(routingContext, resolvedVirtualId, plugi
|
|
|
2045
2155
|
continue;
|
|
2046
2156
|
}
|
|
2047
2157
|
const moduleInfo = await pluginContext.load({
|
|
2048
|
-
id: resolved.id
|
|
2158
|
+
id: resolved.id,
|
|
2159
|
+
resolveDependencies: true
|
|
2049
2160
|
});
|
|
2050
2161
|
if (moduleInfo.code == null) {
|
|
2051
2162
|
continue;
|
|
2052
2163
|
}
|
|
2164
|
+
const qwikDeps = moduleInfo.meta?.qwikdeps ?? [];
|
|
2053
2165
|
if (moduleInfo.code.includes("serverQrl(")) {
|
|
2054
2166
|
serverFnModules.add(moduleInfo.id);
|
|
2055
2167
|
}
|
|
2056
|
-
const resolvedImports =
|
|
2168
|
+
const resolvedImports = [
|
|
2169
|
+
...moduleInfo.importedIds,
|
|
2170
|
+
...moduleInfo.dynamicallyImportedIds,
|
|
2171
|
+
...qwikDeps
|
|
2172
|
+
];
|
|
2057
2173
|
for (let i = 0; i < resolvedImports.length; i++) {
|
|
2058
2174
|
const resolvedImport = resolvedImports[i];
|
|
2059
2175
|
if (resolvedImport && !seenModuleIds.has(resolvedImport)) {
|
|
@@ -2215,7 +2331,7 @@ class HtmlTransformPatcher {
|
|
|
2215
2331
|
try {
|
|
2216
2332
|
const fakeHtml = `<html><head>[FAKE_HEAD]</head><body>[FAKE_BODY]</body></html>`;
|
|
2217
2333
|
const transformedHtml = await this.server.transformIndexHtml(this.request.url || "/", fakeHtml);
|
|
2218
|
-
const hmrBridge = this.server.hot ? `<script type="module" src="
|
|
2334
|
+
const hmrBridge = this.server.hot ? `<script type="module" src="${this.server.config.base}@id/@qwik-hmr-bridge"><\/script>` : "";
|
|
2219
2335
|
const fakeHeadIndex = transformedHtml.indexOf("[FAKE_HEAD]");
|
|
2220
2336
|
const fakeHeadCloseIndex = transformedHtml.indexOf("</head>", fakeHeadIndex);
|
|
2221
2337
|
if (fakeHeadIndex === -1 || fakeHeadCloseIndex === -1) {
|
|
@@ -2296,6 +2412,66 @@ function wrapResponseForHtmlTransform(request, response, server) {
|
|
|
2296
2412
|
return response;
|
|
2297
2413
|
}
|
|
2298
2414
|
|
|
2415
|
+
const FULLPATH_HEADER = "X-Qwik-fullpath";
|
|
2416
|
+
const ROUTE_PATH_HEADER = "X-Qwik-route-path";
|
|
2417
|
+
function getDevMiddlewareRequestPath(req) {
|
|
2418
|
+
return req.originalUrl || req.url;
|
|
2419
|
+
}
|
|
2420
|
+
async function loadSsrModule(server, filePath) {
|
|
2421
|
+
return server.ssrLoadModule(getImportPath(normalizePath(filePath)));
|
|
2422
|
+
}
|
|
2423
|
+
function matchesLoaderId(loader, loaderId) {
|
|
2424
|
+
return loader.__id === loaderId || loader.__qrl.getHash() === loaderId;
|
|
2425
|
+
}
|
|
2426
|
+
function isLoaderInternal(value) {
|
|
2427
|
+
return typeof value === "function" && value.__brand === "server_loader";
|
|
2428
|
+
}
|
|
2429
|
+
function findMatchingLoader(mod, loaderId) {
|
|
2430
|
+
for (const value of Object.values(mod)) {
|
|
2431
|
+
if (isLoaderInternal(value) && matchesLoaderId(value, loaderId)) {
|
|
2432
|
+
return value;
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
async function preloadQLoaderRouteLoader(server, ctx, req) {
|
|
2437
|
+
const requestPath = getDevMiddlewareRequestPath(req);
|
|
2438
|
+
if (!requestPath) {
|
|
2439
|
+
return void 0;
|
|
2440
|
+
}
|
|
2441
|
+
let requestUrl;
|
|
2442
|
+
try {
|
|
2443
|
+
requestUrl = new URL(requestPath, "http://qwik.dev");
|
|
2444
|
+
} catch {
|
|
2445
|
+
return void 0;
|
|
2446
|
+
}
|
|
2447
|
+
const recognized = recognizeRequest(requestUrl.pathname);
|
|
2448
|
+
if (recognized?.type !== IsQLoader || !recognized.data?.loaderId) {
|
|
2449
|
+
return void 0;
|
|
2450
|
+
}
|
|
2451
|
+
const loaderId = recognized.data.loaderId;
|
|
2452
|
+
const headerValue = req.headers[FULLPATH_HEADER.toLowerCase()] ?? req.headers[ROUTE_PATH_HEADER.toLowerCase()];
|
|
2453
|
+
const fullPathHeader = Array.isArray(headerValue) ? headerValue[0] : headerValue ?? null;
|
|
2454
|
+
const loaderPathname = trimRecognizedInternalPathname(requestUrl.pathname, recognized);
|
|
2455
|
+
const pathname = resolveValidInternalFullPathname(loaderPathname, fullPathHeader) ?? loaderPathname;
|
|
2456
|
+
const route = ctx.routes.filter((route2) => isPageExt(route2.ext)).slice().sort(routeSortCompare).find((route2) => route2.pattern.test(pathname));
|
|
2457
|
+
if (!route) {
|
|
2458
|
+
return void 0;
|
|
2459
|
+
}
|
|
2460
|
+
const routeRootFiles = [
|
|
2461
|
+
...route.layouts.map((layout) => layout.filePath),
|
|
2462
|
+
route.filePath
|
|
2463
|
+
];
|
|
2464
|
+
const modules = await Promise.all([
|
|
2465
|
+
...ctx.serverPlugins.map((plugin) => loadSsrModule(server, plugin.filePath)),
|
|
2466
|
+
...routeRootFiles.map((filePath) => loadSsrModule(server, filePath))
|
|
2467
|
+
]);
|
|
2468
|
+
for (const mod of modules) {
|
|
2469
|
+
const loader = findMatchingLoader(mod, loaderId);
|
|
2470
|
+
if (loader) {
|
|
2471
|
+
return loader;
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
}
|
|
2299
2475
|
const makeRouterDevMiddleware = (server, ctx) => async (req, res, next) => {
|
|
2300
2476
|
let mod;
|
|
2301
2477
|
try {
|
|
@@ -2315,6 +2491,11 @@ const makeRouterDevMiddleware = (server, ctx) => async (req, res, next) => {
|
|
|
2315
2491
|
await updateRoutingContext(ctx);
|
|
2316
2492
|
ctx.isDirty = false;
|
|
2317
2493
|
}
|
|
2494
|
+
const preloadedRouteLoader = await preloadQLoaderRouteLoader(server, ctx, req);
|
|
2495
|
+
if (preloadedRouteLoader) {
|
|
2496
|
+
await server.ssrLoadModule("@qwik-router-config");
|
|
2497
|
+
devPreloadedRouteLoaders.set(req, preloadedRouteLoader);
|
|
2498
|
+
}
|
|
2318
2499
|
const entry = ctx.entries.find((e) => req.url === `${server.config.base}${e.chunkFileName}`);
|
|
2319
2500
|
if (entry) {
|
|
2320
2501
|
const entryContents = await server.transformRequest(`/@fs${entry.filePath.startsWith("/") ? "" : "/"}${entry.filePath}`);
|
|
@@ -2392,6 +2573,9 @@ const getCssUrls = (server) => {
|
|
|
2392
2573
|
});
|
|
2393
2574
|
if ((isEntryCSS || hasJSImporter) && !hasCSSImporter && !cssImportedByCSS.has(mod.url)) {
|
|
2394
2575
|
cssModules.add(`${mod.url}${mod.lastHMRTimestamp ? `?t=${mod.lastHMRTimestamp}` : ""}`);
|
|
2576
|
+
if (mod.file) {
|
|
2577
|
+
server.watcher.add(mod.file);
|
|
2578
|
+
}
|
|
2395
2579
|
}
|
|
2396
2580
|
}
|
|
2397
2581
|
}
|
|
@@ -2406,16 +2590,179 @@ const getRouterIndexTags = (server) => {
|
|
|
2406
2590
|
tag: "link",
|
|
2407
2591
|
attrs: {
|
|
2408
2592
|
rel: "stylesheet",
|
|
2409
|
-
href: url
|
|
2593
|
+
href: server.config.base + url.slice(1)
|
|
2410
2594
|
}
|
|
2411
2595
|
}));
|
|
2412
2596
|
};
|
|
2597
|
+
const sendRouterCssHotUpdate = (server, file, timestamp) => {
|
|
2598
|
+
const { client, ssr } = server.environments;
|
|
2599
|
+
if (!isCssPath(file) || client.moduleGraph.getModulesByFile(file)?.size) {
|
|
2600
|
+
return false;
|
|
2601
|
+
}
|
|
2602
|
+
const paths = /* @__PURE__ */ new Set();
|
|
2603
|
+
for (const mod of ssr.moduleGraph.getModulesByFile(file) ?? []) {
|
|
2604
|
+
mod.lastHMRTimestamp = timestamp;
|
|
2605
|
+
paths.add(mod.url.split("?")[0]);
|
|
2606
|
+
}
|
|
2607
|
+
if (!paths.size) {
|
|
2608
|
+
return false;
|
|
2609
|
+
}
|
|
2610
|
+
client.hot.send({
|
|
2611
|
+
type: "update",
|
|
2612
|
+
updates: [
|
|
2613
|
+
...paths
|
|
2614
|
+
].map((path) => ({
|
|
2615
|
+
type: "css-update",
|
|
2616
|
+
path,
|
|
2617
|
+
acceptedPath: path,
|
|
2618
|
+
timestamp
|
|
2619
|
+
}))
|
|
2620
|
+
});
|
|
2621
|
+
return true;
|
|
2622
|
+
};
|
|
2413
2623
|
|
|
2414
2624
|
const QWIK_ROUTER_CONFIG_ID = "@qwik-router-config";
|
|
2415
2625
|
const QWIK_ROUTER_ENTRIES_ID = "@qwik-router-entries";
|
|
2416
2626
|
const QWIK_ROUTER = "@qwik.dev/router";
|
|
2417
2627
|
const QWIK_ROUTER_SW_REGISTER = "@qwik-router-sw-register";
|
|
2418
2628
|
const VIRTUAL_SERVER_FNS = "virtual:qwik-router-server-fns";
|
|
2629
|
+
function replaceLoaderPlaceholders(code, loadersByFile) {
|
|
2630
|
+
return code.replace(/_R\s*:\s*"__LOADERS:([^"]+)__"\s*,?/g, (_match, paths) => {
|
|
2631
|
+
const filePaths = paths.split("|");
|
|
2632
|
+
const hashes = [];
|
|
2633
|
+
for (const filePath of filePaths) {
|
|
2634
|
+
const fileHashes = loadersByFile.get(filePath);
|
|
2635
|
+
if (fileHashes) {
|
|
2636
|
+
hashes.push(...fileHashes);
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2639
|
+
if (hashes.length > 0) {
|
|
2640
|
+
return `_R: ${JSON.stringify(hashes)},`;
|
|
2641
|
+
}
|
|
2642
|
+
return "";
|
|
2643
|
+
});
|
|
2644
|
+
}
|
|
2645
|
+
function addRouteLoaderHash(loadersByFile, filePath, hash) {
|
|
2646
|
+
const normalizedPath = normalizePath(filePath);
|
|
2647
|
+
const existing = loadersByFile.get(normalizedPath);
|
|
2648
|
+
if (!existing) {
|
|
2649
|
+
loadersByFile.set(normalizedPath, [
|
|
2650
|
+
hash
|
|
2651
|
+
]);
|
|
2652
|
+
return true;
|
|
2653
|
+
}
|
|
2654
|
+
if (!existing.includes(hash)) {
|
|
2655
|
+
existing.push(hash);
|
|
2656
|
+
return true;
|
|
2657
|
+
}
|
|
2658
|
+
return false;
|
|
2659
|
+
}
|
|
2660
|
+
function clearRouteLoaderHashes(loadersByFile, filePath) {
|
|
2661
|
+
return loadersByFile.delete(normalizePath(filePath));
|
|
2662
|
+
}
|
|
2663
|
+
function isRouterSourceFilePath(filePath) {
|
|
2664
|
+
const fileName = basename(filePath);
|
|
2665
|
+
const extlessName = removeExtension(fileName);
|
|
2666
|
+
return isMenuFileName(fileName) || isIndexModule(extlessName) || isErrorName(extlessName) || isLayoutModule(extlessName) || isEntryName(extlessName) || isServiceWorkerName(extlessName) || isPluginModule(extlessName);
|
|
2667
|
+
}
|
|
2668
|
+
function isPathInDir(filePath, dirPath) {
|
|
2669
|
+
return filePath === dirPath || filePath.startsWith(`${dirPath}/`);
|
|
2670
|
+
}
|
|
2671
|
+
function isRouterSourceFileForContext(filePath, ctx) {
|
|
2672
|
+
const normalizedPath = normalizePath(filePath);
|
|
2673
|
+
return isRouterSourceFilePath(normalizedPath) && (isPathInDir(normalizedPath, ctx.opts.routesDir) || isPathInDir(normalizedPath, ctx.opts.serverPluginsDir));
|
|
2674
|
+
}
|
|
2675
|
+
function isDiscoveredRouteLoaderSource(filePath, sourceFiles) {
|
|
2676
|
+
if (!sourceFiles) {
|
|
2677
|
+
return false;
|
|
2678
|
+
}
|
|
2679
|
+
const normalizedPath = normalizePath(filePath);
|
|
2680
|
+
for (const files of sourceFiles.values()) {
|
|
2681
|
+
if (files.includes(normalizedPath)) {
|
|
2682
|
+
return true;
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
return false;
|
|
2686
|
+
}
|
|
2687
|
+
function collectRouteLoaderOwners(node, owners) {
|
|
2688
|
+
for (const file of node._files) {
|
|
2689
|
+
if (file.type === "layout" || file.type === "route") {
|
|
2690
|
+
owners.add(file.filePath);
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
for (const child of node.children.values()) {
|
|
2694
|
+
collectRouteLoaderOwners(child, owners);
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
async function collectReExportSources(owner, filePath, resolveSource, sources, seen = /* @__PURE__ */ new Set([
|
|
2698
|
+
owner
|
|
2699
|
+
])) {
|
|
2700
|
+
let code, exportEntries;
|
|
2701
|
+
try {
|
|
2702
|
+
code = await readFile(filePath, "utf-8");
|
|
2703
|
+
exportEntries = findExports(code);
|
|
2704
|
+
if (!exportEntries) {
|
|
2705
|
+
return;
|
|
2706
|
+
}
|
|
2707
|
+
} catch {
|
|
2708
|
+
return;
|
|
2709
|
+
}
|
|
2710
|
+
await Promise.all(exportEntries.map(async (exp) => {
|
|
2711
|
+
if (!exp.specifier) {
|
|
2712
|
+
return;
|
|
2713
|
+
}
|
|
2714
|
+
const resolved = await resolveSource(exp.specifier, filePath);
|
|
2715
|
+
if (!resolved || resolved.charCodeAt(0) === 0) {
|
|
2716
|
+
return;
|
|
2717
|
+
}
|
|
2718
|
+
const resolvedPath = normalizePath(resolved.split(/[?#]/, 1)[0]);
|
|
2719
|
+
let ownerSources = sources.get(owner);
|
|
2720
|
+
if (!ownerSources) {
|
|
2721
|
+
sources.set(owner, ownerSources = []);
|
|
2722
|
+
}
|
|
2723
|
+
if (!ownerSources.includes(resolvedPath)) {
|
|
2724
|
+
ownerSources.push(resolvedPath);
|
|
2725
|
+
}
|
|
2726
|
+
if (!seen.has(resolvedPath)) {
|
|
2727
|
+
seen.add(resolvedPath);
|
|
2728
|
+
await collectReExportSources(owner, resolvedPath, resolveSource, sources, seen);
|
|
2729
|
+
}
|
|
2730
|
+
}));
|
|
2731
|
+
}
|
|
2732
|
+
async function findRouteLoaderSourceFiles(ctx, resolveSource) {
|
|
2733
|
+
const sources = /* @__PURE__ */ new Map();
|
|
2734
|
+
const owners = /* @__PURE__ */ new Set();
|
|
2735
|
+
collectRouteLoaderOwners(ctx.routeTrie, owners);
|
|
2736
|
+
for (const plugin of ctx.serverPlugins) {
|
|
2737
|
+
owners.add(plugin.filePath);
|
|
2738
|
+
}
|
|
2739
|
+
await Promise.all([
|
|
2740
|
+
...owners
|
|
2741
|
+
].map((owner) => collectReExportSources(owner, owner, resolveSource, sources)));
|
|
2742
|
+
return sources;
|
|
2743
|
+
}
|
|
2744
|
+
function invalidateRouterConfigModules(server) {
|
|
2745
|
+
const modules = [];
|
|
2746
|
+
const moduleGraphs = /* @__PURE__ */ new Set();
|
|
2747
|
+
moduleGraphs.add(server.moduleGraph);
|
|
2748
|
+
const environments = server.environments;
|
|
2749
|
+
if (environments) {
|
|
2750
|
+
for (const environment of Object.values(environments)) {
|
|
2751
|
+
const graph = environment?.moduleGraph;
|
|
2752
|
+
if (graph) {
|
|
2753
|
+
moduleGraphs.add(graph);
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
for (const graph of moduleGraphs) {
|
|
2758
|
+
const mod = graph.getModuleById(QWIK_ROUTER_CONFIG_ID);
|
|
2759
|
+
if (mod) {
|
|
2760
|
+
graph.invalidateModule(mod);
|
|
2761
|
+
modules.push(mod);
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2764
|
+
return modules;
|
|
2765
|
+
}
|
|
2419
2766
|
async function generateServerPackageJson(outDir) {
|
|
2420
2767
|
await fs.promises.mkdir(outDir, {
|
|
2421
2768
|
recursive: true
|
|
@@ -2447,6 +2794,9 @@ function qwikRouterPlugin(userOpts, buildContextRef) {
|
|
|
2447
2794
|
let devSsrServer = userOpts?.devSsrServer;
|
|
2448
2795
|
const routesDir = userOpts?.routesDir ?? "src/routes";
|
|
2449
2796
|
const serverPluginsDir = userOpts?.serverPluginsDir ?? routesDir;
|
|
2797
|
+
const loadersByFile = /* @__PURE__ */ new Map();
|
|
2798
|
+
let reExportedRouteLoaderSources;
|
|
2799
|
+
let ssgRoutePatterns;
|
|
2450
2800
|
const api = {
|
|
2451
2801
|
getBasePathname: () => ctx?.opts.basePathname ?? "/",
|
|
2452
2802
|
getRoutes: () => {
|
|
@@ -2454,6 +2804,12 @@ function qwikRouterPlugin(userOpts, buildContextRef) {
|
|
|
2454
2804
|
},
|
|
2455
2805
|
getServiceWorkers: () => {
|
|
2456
2806
|
return ctx?.serviceWorkers.slice() ?? [];
|
|
2807
|
+
},
|
|
2808
|
+
_setSsgRoutes: (include, exclude) => {
|
|
2809
|
+
ssgRoutePatterns = include?.length ? {
|
|
2810
|
+
include,
|
|
2811
|
+
exclude
|
|
2812
|
+
} : void 0;
|
|
2457
2813
|
}
|
|
2458
2814
|
};
|
|
2459
2815
|
const plugin = {
|
|
@@ -2466,7 +2822,8 @@ function qwikRouterPlugin(userOpts, buildContextRef) {
|
|
|
2466
2822
|
define: {
|
|
2467
2823
|
"globalThis.__DEFAULT_LOADERS_SERIALIZATION_STRATEGY__": JSON.stringify(userOpts?.defaultLoadersSerializationStrategy || "never"),
|
|
2468
2824
|
"globalThis.__NO_TRAILING_SLASH__": JSON.stringify(userOpts?.trailingSlash === false),
|
|
2469
|
-
"globalThis.__SSR_CACHE_SIZE__": JSON.stringify(viteEnv.command === "serve" ? 0 : userOpts?.ssrCacheSize ?? 50)
|
|
2825
|
+
"globalThis.__SSR_CACHE_SIZE__": JSON.stringify(viteEnv.command === "serve" ? 0 : userOpts?.ssrCacheSize ?? 50),
|
|
2826
|
+
"globalThis.__STRICT_LOADERS__": JSON.stringify(userOpts?.strictLoaders !== false)
|
|
2470
2827
|
},
|
|
2471
2828
|
appType: "custom",
|
|
2472
2829
|
resolve: {
|
|
@@ -2536,7 +2893,7 @@ function qwikRouterPlugin(userOpts, buildContextRef) {
|
|
|
2536
2893
|
return updatedViteConfig;
|
|
2537
2894
|
},
|
|
2538
2895
|
configEnvironment(name, _config, _env) {
|
|
2539
|
-
if (name === "ssr") {
|
|
2896
|
+
if (name === "ssr" || name === "ssg") {
|
|
2540
2897
|
return {
|
|
2541
2898
|
resolve: {
|
|
2542
2899
|
external: [
|
|
@@ -2566,6 +2923,14 @@ function qwikRouterPlugin(userOpts, buildContextRef) {
|
|
|
2566
2923
|
if (!qwikPlugin) {
|
|
2567
2924
|
throw new Error("Missing vite-plugin-qwik");
|
|
2568
2925
|
}
|
|
2926
|
+
qwikPlugin.api.onSegment((parentId, segment) => {
|
|
2927
|
+
if (segment.ctxName === "routeLoader$") {
|
|
2928
|
+
const changed = addRouteLoaderHash(loadersByFile, parentId, segment.hash);
|
|
2929
|
+
if (changed && devServer) {
|
|
2930
|
+
invalidateRouterConfigModules(devServer);
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
});
|
|
2569
2934
|
if (typeof devSsrServer !== "boolean") {
|
|
2570
2935
|
devSsrServer = qwikPlugin.api._oldDevSsrServer();
|
|
2571
2936
|
}
|
|
@@ -2576,21 +2941,19 @@ function qwikRouterPlugin(userOpts, buildContextRef) {
|
|
|
2576
2941
|
},
|
|
2577
2942
|
async configureServer(server) {
|
|
2578
2943
|
devServer = server;
|
|
2579
|
-
const toWatch =
|
|
2944
|
+
const toWatch = [
|
|
2945
|
+
join(ctx.opts.routesDir, "**/{index,index!,index@*,layout,layout!,layout-*,error,404,entry,service-worker,menu}.{ts,tsx,js,jsx,md,mdx}"),
|
|
2946
|
+
join(ctx.opts.serverPluginsDir, "plugin{,@*}.{ts,tsx,js,jsx}")
|
|
2947
|
+
];
|
|
2580
2948
|
server.watcher.add(toWatch);
|
|
2581
2949
|
await new Promise((resolve2) => setTimeout(resolve2, 1e3));
|
|
2582
2950
|
server.watcher.on("change", (path) => {
|
|
2583
|
-
if (
|
|
2951
|
+
if (!isRouterSourceFileForContext(path, ctx)) {
|
|
2584
2952
|
return;
|
|
2585
2953
|
}
|
|
2954
|
+
clearRouteLoaderHashes(loadersByFile, path);
|
|
2586
2955
|
ctx.isDirty = true;
|
|
2587
|
-
|
|
2588
|
-
if (graph) {
|
|
2589
|
-
const mod = graph.getModuleById("@qwik-router-config");
|
|
2590
|
-
if (mod) {
|
|
2591
|
-
graph.invalidateModule(mod);
|
|
2592
|
-
}
|
|
2593
|
-
}
|
|
2956
|
+
invalidateRouterConfigModules(server);
|
|
2594
2957
|
});
|
|
2595
2958
|
if (userOpts?.devSsrServer !== false) {
|
|
2596
2959
|
return () => {
|
|
@@ -2598,6 +2961,31 @@ function qwikRouterPlugin(userOpts, buildContextRef) {
|
|
|
2598
2961
|
};
|
|
2599
2962
|
}
|
|
2600
2963
|
},
|
|
2964
|
+
handleHotUpdate({ file, modules, server, timestamp }) {
|
|
2965
|
+
if (sendRouterCssHotUpdate(server, file, timestamp)) {
|
|
2966
|
+
return [];
|
|
2967
|
+
}
|
|
2968
|
+
const clearedLoaderHashes = clearRouteLoaderHashes(loadersByFile, file);
|
|
2969
|
+
if (!ctx) {
|
|
2970
|
+
return;
|
|
2971
|
+
}
|
|
2972
|
+
if (!isRouterSourceFileForContext(file, ctx)) {
|
|
2973
|
+
if (clearedLoaderHashes || isDiscoveredRouteLoaderSource(file, reExportedRouteLoaderSources)) {
|
|
2974
|
+
const configModules2 = invalidateRouterConfigModules(server);
|
|
2975
|
+
return [
|
|
2976
|
+
...modules,
|
|
2977
|
+
...configModules2
|
|
2978
|
+
];
|
|
2979
|
+
}
|
|
2980
|
+
return;
|
|
2981
|
+
}
|
|
2982
|
+
ctx.isDirty = true;
|
|
2983
|
+
const configModules = invalidateRouterConfigModules(server);
|
|
2984
|
+
return [
|
|
2985
|
+
...modules,
|
|
2986
|
+
...configModules
|
|
2987
|
+
];
|
|
2988
|
+
},
|
|
2601
2989
|
transformIndexHtml() {
|
|
2602
2990
|
if (viteCommand !== "serve") {
|
|
2603
2991
|
return;
|
|
@@ -2638,7 +3026,15 @@ function qwikRouterPlugin(userOpts, buildContextRef) {
|
|
|
2638
3026
|
});
|
|
2639
3027
|
}
|
|
2640
3028
|
if (isRouterConfig) {
|
|
2641
|
-
|
|
3029
|
+
const isServerConsumer = this.environment.config.consumer === "server";
|
|
3030
|
+
const serverExcludePaths = isServerConsumer && !devServer && this.environment.name !== "ssg" ? await getServerExcludedRoutes(ctx, ssgRoutePatterns) : void 0;
|
|
3031
|
+
reExportedRouteLoaderSources = await findRouteLoaderSourceFiles(ctx, async (specifier, importer) => {
|
|
3032
|
+
const resolved = await this.resolve(specifier, importer, {
|
|
3033
|
+
skipSelf: true
|
|
3034
|
+
});
|
|
3035
|
+
return resolved?.id;
|
|
3036
|
+
});
|
|
3037
|
+
return generateQwikRouterConfig(ctx, qwikPlugin, isServerConsumer, devServer ? loadersByFile : void 0, serverExcludePaths, reExportedRouteLoaderSources);
|
|
2642
3038
|
}
|
|
2643
3039
|
if (isSwRegister) {
|
|
2644
3040
|
return generateServiceWorkerRegister(ctx, swRegister);
|
|
@@ -2692,6 +3088,20 @@ function qwikRouterPlugin(userOpts, buildContextRef) {
|
|
|
2692
3088
|
return null;
|
|
2693
3089
|
},
|
|
2694
3090
|
generateBundle(_, bundles) {
|
|
3091
|
+
const manifest = qwikPlugin.api.getManifest();
|
|
3092
|
+
if (manifest) {
|
|
3093
|
+
const srcDir = qwikPlugin.api.getOptions().srcDir;
|
|
3094
|
+
for (const symbol of Object.values(manifest.symbols)) {
|
|
3095
|
+
if (symbol.ctxName === "routeLoader$" && symbol.origin) {
|
|
3096
|
+
addRouteLoaderHash(loadersByFile, resolve(srcDir, symbol.origin), symbol.hash);
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
for (const chunk of Object.values(bundles)) {
|
|
3101
|
+
if (chunk.type === "chunk" && chunk.code.includes("__LOADERS:")) {
|
|
3102
|
+
chunk.code = replaceLoaderPlaceholders(chunk.code, loadersByFile);
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
2695
3105
|
if (this.environment.config.consumer === "client") {
|
|
2696
3106
|
const entries = [
|
|
2697
3107
|
...ctx.entries,
|
|
@@ -2779,7 +3189,8 @@ function serverFnsPlugin(buildContextRef) {
|
|
|
2779
3189
|
}
|
|
2780
3190
|
return [
|
|
2781
3191
|
...serverFnModules
|
|
2782
|
-
].map((mod) => `import ${JSON.stringify(mod)}
|
|
3192
|
+
].map((mod, index) => `import * as serverFnModule${index} from ${JSON.stringify(mod)};
|
|
3193
|
+
Object.values(serverFnModule${index});`).join("\n");
|
|
2783
3194
|
}
|
|
2784
3195
|
return null;
|
|
2785
3196
|
}
|