@askrjs/cli 0.0.22 → 0.0.23
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/ssg-config.d.ts +2 -0
- package/dist/ssg.js +20 -4
- package/package.json +1 -1
package/dist/ssg-config.d.ts
CHANGED
|
@@ -64,6 +64,8 @@ interface SsgOutputBudgets {
|
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
66
|
interface SsgOutputReportConfig {
|
|
67
|
+
/** Deployment pathname prefix stripped from root-absolute asset references. */
|
|
68
|
+
basePath?: string;
|
|
67
69
|
budgets?: SsgOutputBudgets;
|
|
68
70
|
/** Number of largest pages retained in the summary. Defaults to 20. */
|
|
69
71
|
largestPages?: number;
|
package/dist/ssg.js
CHANGED
|
@@ -404,7 +404,7 @@ function isReportableAsset(filePath) {
|
|
|
404
404
|
async function removeSsgOutputReport(outputDir) {
|
|
405
405
|
await fs.rm(path.join(outputDir, REPORT_PATH), { force: true });
|
|
406
406
|
}
|
|
407
|
-
function localReference(reference, documentPath) {
|
|
407
|
+
function localReference(reference, documentPath, basePath) {
|
|
408
408
|
let resolved;
|
|
409
409
|
try {
|
|
410
410
|
const portableDocumentPath = documentPath.replaceAll("\\", "/");
|
|
@@ -416,11 +416,25 @@ function localReference(reference, documentPath) {
|
|
|
416
416
|
}
|
|
417
417
|
if (resolved.origin !== "https://askr.invalid") return void 0;
|
|
418
418
|
try {
|
|
419
|
-
|
|
419
|
+
let pathname = decodeURIComponent(resolved.pathname);
|
|
420
|
+
if (basePath && reference.startsWith("/") && (pathname === basePath || pathname.startsWith(`${basePath}/`))) pathname = pathname.slice(basePath.length);
|
|
421
|
+
return pathname.replace(/^\/+/, "");
|
|
420
422
|
} catch {
|
|
421
423
|
return;
|
|
422
424
|
}
|
|
423
425
|
}
|
|
426
|
+
function deploymentBasePath(value) {
|
|
427
|
+
if (value === void 0 || value === "" || value === "/") return "";
|
|
428
|
+
if (typeof value !== "string" || !value.startsWith("/") || value.startsWith("//") || value.includes("?") || value.includes("#") || value.includes("\\")) throw new Error("outputReport.basePath must be an absolute URL pathname");
|
|
429
|
+
let decoded;
|
|
430
|
+
try {
|
|
431
|
+
decoded = decodeURIComponent(value);
|
|
432
|
+
} catch {
|
|
433
|
+
throw new Error("outputReport.basePath must use valid URL encoding");
|
|
434
|
+
}
|
|
435
|
+
if (decoded.split("/").some((segment) => segment === "." || segment === "..")) throw new Error("outputReport.basePath must not contain dot-segment traversal");
|
|
436
|
+
return decoded.replace(/\/+$/, "");
|
|
437
|
+
}
|
|
424
438
|
function positiveCount(value, fallback, label) {
|
|
425
439
|
const resolved = value ?? fallback;
|
|
426
440
|
if (!Number.isSafeInteger(resolved) || resolved < 1) throw new Error(`${label} must be a positive integer`);
|
|
@@ -433,6 +447,7 @@ function validateByteBudget(budget, label) {
|
|
|
433
447
|
}
|
|
434
448
|
}
|
|
435
449
|
function validateConfig(config) {
|
|
450
|
+
const basePath = deploymentBasePath(config.basePath);
|
|
436
451
|
const budgets = config.budgets;
|
|
437
452
|
validateByteBudget(budgets?.routes, "outputReport.budgets.routes");
|
|
438
453
|
for (const [route, budget] of Object.entries(budgets?.routeOverrides ?? {})) if (budget !== false) validateByteBudget(budget, `route override ${route}`);
|
|
@@ -443,6 +458,7 @@ function validateConfig(config) {
|
|
|
443
458
|
for (const [route, share] of hydrationShares) if (share !== void 0 && share !== false && (!Number.isFinite(share) || share < 0 || share > 1)) throw new Error(`hydration share for ${route} must be from 0 through 1`);
|
|
444
459
|
positiveCount(config.largestPages, 20, "outputReport.largestPages");
|
|
445
460
|
positiveCount(config.largestAssets, 20, "outputReport.largestAssets");
|
|
461
|
+
return basePath;
|
|
446
462
|
}
|
|
447
463
|
function budgetViolations(report, budgets = {}) {
|
|
448
464
|
const violations = [];
|
|
@@ -474,7 +490,7 @@ function budgetViolations(report, budgets = {}) {
|
|
|
474
490
|
return violations;
|
|
475
491
|
}
|
|
476
492
|
async function writeSsgOutputReport(outputDir, routes, inspections, config = {}) {
|
|
477
|
-
validateConfig(config);
|
|
493
|
+
const basePath = validateConfig(config);
|
|
478
494
|
const assets = [];
|
|
479
495
|
for (const filePath of (await emittedFiles(outputDir)).filter(isReportableAsset)) {
|
|
480
496
|
const measured = size(await fs.readFile(path.join(outputDir, filePath)));
|
|
@@ -495,7 +511,7 @@ async function writeSsgOutputReport(outputDir, routes, inspections, config = {})
|
|
|
495
511
|
const referenced = (references, type) => {
|
|
496
512
|
const resolved = [];
|
|
497
513
|
for (const reference of references) {
|
|
498
|
-
const localPath = localReference(reference, inspection.filePath);
|
|
514
|
+
const localPath = localReference(reference, inspection.filePath, basePath);
|
|
499
515
|
if (!localPath) continue;
|
|
500
516
|
const asset = assetMap.get(localPath);
|
|
501
517
|
if (!asset) {
|