@ecopages/core 0.2.0-beta.28 → 0.2.0-beta.29
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/package.json +2 -2
- package/src/adapters/abstract/application-adapter.d.ts +22 -4
- package/src/adapters/abstract/application-adapter.js +27 -4
- package/src/adapters/abstract/server-adapter.d.ts +10 -0
- package/src/adapters/bun/create-app.d.ts +1 -0
- package/src/adapters/bun/create-app.js +11 -4
- package/src/adapters/bun/server-adapter.js +1 -0
- package/src/adapters/node/create-app.d.ts +1 -0
- package/src/adapters/node/create-app.js +10 -3
- package/src/adapters/node/server-adapter.js +1 -0
- package/src/adapters/shared/fs-server-response-factory.d.ts +5 -0
- package/src/adapters/shared/fs-server-response-factory.js +20 -0
- package/src/adapters/shared/fs-server-response-matcher.d.ts +18 -0
- package/src/adapters/shared/fs-server-response-matcher.js +75 -15
- package/src/config/config-builder.d.ts +12 -2
- package/src/config/config-builder.js +41 -2
- package/src/config/constants.d.ts +1 -0
- package/src/config/constants.js +2 -1
- package/src/route-renderer/orchestration/route-pipeline/robots-meta.contribution.d.ts +15 -0
- package/src/route-renderer/orchestration/route-pipeline/robots-meta.contribution.js +36 -0
- package/src/route-renderer/orchestration/route-pipeline/route-html-finalization.service.d.ts +4 -0
- package/src/route-renderer/orchestration/route-pipeline/route-html-finalization.service.js +5 -2
- package/src/route-renderer/orchestration/route-pipeline/route-prepared-options.builder.js +6 -2
- package/src/static-site-generator/README.md +9 -1
- package/src/static-site-generator/sitemap-routes.d.ts +14 -0
- package/src/static-site-generator/sitemap-routes.js +31 -0
- package/src/static-site-generator/sitemap.d.ts +15 -0
- package/src/static-site-generator/sitemap.js +33 -0
- package/src/static-site-generator/static-export-context.d.ts +9 -1
- package/src/static-site-generator/static-site-generator.d.ts +27 -6
- package/src/static-site-generator/static-site-generator.js +200 -50
- package/src/types/internal-types.d.ts +8 -1
- package/src/types/public-types.d.ts +77 -1
- package/src/utils/ecopages-route-info.d.ts +26 -0
- package/src/utils/ecopages-route-info.js +26 -0
- package/src/utils/html-escaping.d.ts +7 -0
- package/src/utils/html-escaping.js +5 -1
- package/src/utils/path-pattern.d.ts +23 -0
- package/src/utils/path-pattern.js +27 -0
|
@@ -5,7 +5,7 @@ import type { BuildRuntime } from '../build/runtime/build-runtime.js';
|
|
|
5
5
|
import type { AnyIntegrationPlugin } from '../plugins/integration-plugin.js';
|
|
6
6
|
import type { Processor } from '../plugins/processor.js';
|
|
7
7
|
import type { EcoSourceTransform } from '../plugins/source-transform.js';
|
|
8
|
-
import type { PageMetadataProps } from './public-types.js';
|
|
8
|
+
import type { PageMetadataProps, SitemapConfig } from './public-types.js';
|
|
9
9
|
import type { RouteRegistry } from '../router/server/route-registry.js';
|
|
10
10
|
import type { CacheConfig } from '../services/cache/cache.types.js';
|
|
11
11
|
import type { DevGraphService } from '../services/runtime-state/dev-graph.service.js';
|
|
@@ -99,6 +99,12 @@ export type EcoPagesAppConfig = {
|
|
|
99
99
|
*/
|
|
100
100
|
preferences: RobotsPreference;
|
|
101
101
|
};
|
|
102
|
+
/**
|
|
103
|
+
* Automatic sitemap.xml generation during static export.
|
|
104
|
+
*
|
|
105
|
+
* @default { enabled: false, fileName: 'sitemap.xml', extraUrls: [], exclude: [] }
|
|
106
|
+
*/
|
|
107
|
+
sitemap: SitemapConfig;
|
|
102
108
|
/** Additional paths to watch. Use this to monitor extra files. It is relative to the rootDir */
|
|
103
109
|
additionalWatchPaths: string[];
|
|
104
110
|
/**
|
|
@@ -123,6 +129,7 @@ export type EcoPagesAppConfig = {
|
|
|
123
129
|
srcDir: string;
|
|
124
130
|
htmlTemplatePath: string;
|
|
125
131
|
error404TemplatePath: string;
|
|
132
|
+
error500TemplatePath: string;
|
|
126
133
|
};
|
|
127
134
|
/**
|
|
128
135
|
* The processors to be used in the app
|
|
@@ -534,6 +534,21 @@ export type EcoComponent<P = any, R = any> = IsAny<P> extends true ? EcoFunction
|
|
|
534
534
|
export type PageProps<T = unknown> = T & StaticPageContext & {
|
|
535
535
|
locals?: RequestLocals;
|
|
536
536
|
};
|
|
537
|
+
/**
|
|
538
|
+
* Page-level robots directives for the document head and sitemap filtering.
|
|
539
|
+
*
|
|
540
|
+
* @remarks
|
|
541
|
+
* When `index` is `false`, the page is omitted from the auto-generated sitemap
|
|
542
|
+
* for routes whose metadata can be resolved during static export (filesystem
|
|
543
|
+
* pages and `app.static()` views with `metadata`). Defaults when omitted:
|
|
544
|
+
* `index: true`, `follow: true`. A `<meta name="robots">` tag is emitted by the
|
|
545
|
+
* route HTML finalization path when directives differ from those defaults.
|
|
546
|
+
*/
|
|
547
|
+
export interface PageRobotsMetadata {
|
|
548
|
+
index?: boolean;
|
|
549
|
+
follow?: boolean;
|
|
550
|
+
nocache?: boolean;
|
|
551
|
+
}
|
|
537
552
|
/**
|
|
538
553
|
* Represents the metadata for a page.
|
|
539
554
|
*/
|
|
@@ -543,6 +558,48 @@ export interface PageMetadataProps {
|
|
|
543
558
|
image?: string;
|
|
544
559
|
url?: string;
|
|
545
560
|
keywords?: string[];
|
|
561
|
+
robots?: PageRobotsMetadata;
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Configuration for automatic `sitemap.xml` generation during static export.
|
|
565
|
+
*
|
|
566
|
+
* @remarks
|
|
567
|
+
* Disabled by default. When enabled, the sitemap is written after
|
|
568
|
+
* `afterStaticExport` so integration-generated URLs can be listed via
|
|
569
|
+
* `extraUrls`. Use `exclude` for bulk pathname filters and `metadata.robots.index: false`
|
|
570
|
+
* for page-level noindex that also removes the URL from the sitemap when metadata
|
|
571
|
+
* is available during export.
|
|
572
|
+
*/
|
|
573
|
+
export interface SitemapConfig {
|
|
574
|
+
/** Emit sitemap.xml during static generation. @default false */
|
|
575
|
+
enabled?: boolean;
|
|
576
|
+
/** Output file name. @default "sitemap.xml" */
|
|
577
|
+
fileName?: string;
|
|
578
|
+
/**
|
|
579
|
+
* Extra, non-page URLs to include (e.g. "/rss.xml").
|
|
580
|
+
*
|
|
581
|
+
* @remarks
|
|
582
|
+
* Relative paths are resolved against `baseUrl`. Absolute `http(s)` URLs pass
|
|
583
|
+
* through. Always appended after page URLs; not filtered by `exclude` or page robots.
|
|
584
|
+
*/
|
|
585
|
+
extraUrls?: string[];
|
|
586
|
+
/**
|
|
587
|
+
* Pathname patterns to exclude even if a page exists.
|
|
588
|
+
*
|
|
589
|
+
* @remarks
|
|
590
|
+
* Supported patterns: exact paths (`/admin`) and prefix wildcards (`/admin/**`).
|
|
591
|
+
* Unsupported patterns are treated as exact matches. This is not a full glob engine.
|
|
592
|
+
*/
|
|
593
|
+
exclude?: string[];
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Slim route descriptor for static-export hooks and runtime consumers.
|
|
597
|
+
*/
|
|
598
|
+
export interface EcopagesRouteInfo {
|
|
599
|
+
/** Resolved URL pathname, e.g. `/blog/my-post`. */
|
|
600
|
+
pathname: string;
|
|
601
|
+
/** Route params for dynamic routes (empty for static routes). */
|
|
602
|
+
params: Record<string, string>;
|
|
546
603
|
}
|
|
547
604
|
/**
|
|
548
605
|
* Represents the props for the head of a page.
|
|
@@ -622,12 +679,25 @@ export type EcoLayoutComponent<T = EcoPagesElement> = EcoComponent<LayoutProps<T
|
|
|
622
679
|
*/
|
|
623
680
|
export type EcoHtmlComponent<T = EcoPagesElement> = EcoComponent<HtmlTemplateProps, T>;
|
|
624
681
|
/**
|
|
625
|
-
*
|
|
682
|
+
* Props type for the semantic `404.*` page template.
|
|
683
|
+
* @remarks `message` and `stack` are declared for future error context. The
|
|
684
|
+
* runtime does not currently pass these props when rendering the custom 404 page.
|
|
626
685
|
*/
|
|
627
686
|
export interface Error404TemplateProps extends Omit<HtmlTemplateProps, 'children'> {
|
|
628
687
|
message: string;
|
|
629
688
|
stack?: string;
|
|
630
689
|
}
|
|
690
|
+
/**
|
|
691
|
+
* Props type for the semantic `500.*` page template.
|
|
692
|
+
* @remarks In development, the page-pipeline passes `message` and `stack` from the
|
|
693
|
+
* thrown error when rendering this page after a failure. In production those
|
|
694
|
+
* fields are omitted so stacks are not serialized into HTML. Direct visits to
|
|
695
|
+
* `/500` also omit them.
|
|
696
|
+
*/
|
|
697
|
+
export interface Error500TemplateProps extends Omit<HtmlTemplateProps, 'children'> {
|
|
698
|
+
message?: string;
|
|
699
|
+
stack?: string;
|
|
700
|
+
}
|
|
631
701
|
/**
|
|
632
702
|
* Represents the parameters for a page.
|
|
633
703
|
* The keys are strings, and the values can be either a string or an array of strings.
|
|
@@ -751,6 +821,12 @@ export type RouteRendererOptions = {
|
|
|
751
821
|
params?: PageParams;
|
|
752
822
|
query?: PageQuery;
|
|
753
823
|
locals?: RequestLocals;
|
|
824
|
+
/**
|
|
825
|
+
* Extra props merged into the page props for this render.
|
|
826
|
+
* @remarks Used by semantic error pages (for example development `message` /
|
|
827
|
+
* `stack` on the custom 500 page).
|
|
828
|
+
*/
|
|
829
|
+
props?: Record<string, unknown>;
|
|
754
830
|
};
|
|
755
831
|
/**
|
|
756
832
|
* The body of the route renderer.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { EcopagesRouteInfo } from '../types/public-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Maps a static-generation or registry route into the public slim route shape.
|
|
4
|
+
*/
|
|
5
|
+
export declare function toEcopagesRouteInfo(route: {
|
|
6
|
+
pathname: string;
|
|
7
|
+
params: Record<string, string | string[]>;
|
|
8
|
+
}): EcopagesRouteInfo;
|
|
9
|
+
/**
|
|
10
|
+
* Flattens string | string[] route params into string values for public APIs.
|
|
11
|
+
*/
|
|
12
|
+
export declare function normalizeRouteParams(params: Record<string, string | string[]>): Record<string, string>;
|
|
13
|
+
type StaticGenerationRouteLister = (input: {
|
|
14
|
+
runtimeOrigin: string;
|
|
15
|
+
}) => Promise<readonly {
|
|
16
|
+
pathname: string;
|
|
17
|
+
params: Record<string, string | string[]>;
|
|
18
|
+
}[]>;
|
|
19
|
+
/**
|
|
20
|
+
* Resolves {@link EcopagesRouteInfo} entries for app-start and similar consumers.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveAppStartRoutes(input: {
|
|
23
|
+
listStaticGenerationRoutes?: StaticGenerationRouteLister;
|
|
24
|
+
runtimeOrigin: string;
|
|
25
|
+
}): Promise<EcopagesRouteInfo[]>;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { normalizePathname } from "./path-pattern.js";
|
|
2
|
+
function toEcopagesRouteInfo(route) {
|
|
3
|
+
return {
|
|
4
|
+
pathname: normalizePathname(route.pathname),
|
|
5
|
+
params: normalizeRouteParams(route.params)
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
function normalizeRouteParams(params) {
|
|
9
|
+
const normalized = {};
|
|
10
|
+
for (const [key, value] of Object.entries(params)) {
|
|
11
|
+
normalized[key] = Array.isArray(value) ? value.join("/") : value;
|
|
12
|
+
}
|
|
13
|
+
return normalized;
|
|
14
|
+
}
|
|
15
|
+
async function resolveAppStartRoutes(input) {
|
|
16
|
+
if (!input.listStaticGenerationRoutes) {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
const routes = await input.listStaticGenerationRoutes({ runtimeOrigin: input.runtimeOrigin });
|
|
20
|
+
return routes.map(toEcopagesRouteInfo);
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
normalizeRouteParams,
|
|
24
|
+
resolveAppStartRoutes,
|
|
25
|
+
toEcopagesRouteInfo
|
|
26
|
+
};
|
|
@@ -5,3 +5,10 @@
|
|
|
5
5
|
* @returns Escaped attribute-safe string.
|
|
6
6
|
*/
|
|
7
7
|
export declare function escapeHtmlAttribute(value: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Escapes a string for safe use as XML text content (e.g. `<loc>` values).
|
|
10
|
+
*
|
|
11
|
+
* @param value Raw text node value.
|
|
12
|
+
* @returns Escaped XML-safe string.
|
|
13
|
+
*/
|
|
14
|
+
export declare function escapeXmlText(value: string): string;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
function escapeHtmlAttribute(value) {
|
|
2
2
|
return value.replaceAll("&", "&").replaceAll('"', """).replaceAll("<", "<").replaceAll(">", ">");
|
|
3
3
|
}
|
|
4
|
+
function escapeXmlText(value) {
|
|
5
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
6
|
+
}
|
|
4
7
|
export {
|
|
5
|
-
escapeHtmlAttribute
|
|
8
|
+
escapeHtmlAttribute,
|
|
9
|
+
escapeXmlText
|
|
6
10
|
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalizes a URL pathname for matching and sitemap location building.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Ensures a leading slash and strips trailing slashes except for `/`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function normalizePathname(pathname: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Matches a pathname against a small set of supported patterns.
|
|
10
|
+
*
|
|
11
|
+
* @remarks
|
|
12
|
+
* Supported forms:
|
|
13
|
+
* - exact: `/admin`
|
|
14
|
+
* - prefix wildcard: `/admin/**` (matches `/admin` and descendants)
|
|
15
|
+
* - trailing `**` only as a full segment after `/`
|
|
16
|
+
*
|
|
17
|
+
* This is not a full glob engine. Unsupported patterns are treated as exact matches.
|
|
18
|
+
*/
|
|
19
|
+
export declare function matchPathPattern(pathname: string, pattern: string): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Returns true when the pathname matches any of the patterns.
|
|
22
|
+
*/
|
|
23
|
+
export declare function matchesAnyPathPattern(pathname: string, patterns: readonly string[]): boolean;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
function normalizePathname(pathname) {
|
|
2
|
+
if (!pathname || pathname === "/") {
|
|
3
|
+
return "/";
|
|
4
|
+
}
|
|
5
|
+
const withLeadingSlash = pathname.startsWith("/") ? pathname : `/${pathname}`;
|
|
6
|
+
return withLeadingSlash.replace(/\/+$/, "") || "/";
|
|
7
|
+
}
|
|
8
|
+
function matchPathPattern(pathname, pattern) {
|
|
9
|
+
const normalizedPath = normalizePathname(pathname);
|
|
10
|
+
const normalizedPattern = pattern.trim();
|
|
11
|
+
if (normalizedPattern.endsWith("/**")) {
|
|
12
|
+
const prefix = normalizePathname(normalizedPattern.slice(0, -3));
|
|
13
|
+
if (prefix === "/") {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
return normalizedPath === prefix || normalizedPath.startsWith(`${prefix}/`);
|
|
17
|
+
}
|
|
18
|
+
return normalizedPath === normalizePathname(normalizedPattern);
|
|
19
|
+
}
|
|
20
|
+
function matchesAnyPathPattern(pathname, patterns) {
|
|
21
|
+
return patterns.some((pattern) => matchPathPattern(pathname, pattern));
|
|
22
|
+
}
|
|
23
|
+
export {
|
|
24
|
+
matchPathPattern,
|
|
25
|
+
matchesAnyPathPattern,
|
|
26
|
+
normalizePathname
|
|
27
|
+
};
|