@gracile/engine 0.9.0-next.5 → 0.9.0-next.6
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/dev/development.d.ts.map +1 -1
- package/dist/dev/development.js +1 -1
- package/dist/render/route-template.d.ts.map +1 -1
- package/dist/render/route-template.js +5 -1
- package/dist/routes/collect.d.ts +2 -2
- package/dist/routes/collect.d.ts.map +1 -1
- package/dist/routes/collect.js +7 -6
- package/dist/routes/match.d.ts +6 -2
- package/dist/routes/match.d.ts.map +1 -1
- package/dist/routes/match.js +19 -3
- package/dist/routes/render.d.ts.map +1 -1
- package/dist/routes/render.js +9 -2
- package/dist/server/request.d.ts.map +1 -1
- package/dist/server/request.js +20 -3
- package/dist/user-config.d.ts +13 -0
- package/dist/user-config.d.ts.map +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"development.d.ts","sourceRoot":"","sources":["../../src/dev/development.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,MAAM,CAAC;AAG1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAEN,KAAK,cAAc,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAIvD,wBAAsB,wBAAwB,CAAC,EAC9C,MAAM,EACN,IAAI,EACJ,aAAa,GACb,EAAE;IACF,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,aAAa,CAAC;IACpB,aAAa,EAAE,aAAa,CAAC;CAC7B,GAAG,OAAO,CAAC;IACX,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,cAAc,CAAC;CACvB,CAAC,
|
|
1
|
+
{"version":3,"file":"development.d.ts","sourceRoot":"","sources":["../../src/dev/development.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,MAAM,CAAC;AAG1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAEN,KAAK,cAAc,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAIvD,wBAAsB,wBAAwB,CAAC,EAC9C,MAAM,EACN,IAAI,EACJ,aAAa,GACb,EAAE;IACF,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,aAAa,CAAC;IACpB,aAAa,EAAE,aAAa,CAAC;CAC7B,GAAG,OAAO,CAAC;IACX,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,cAAc,CAAC;CACvB,CAAC,CAqDD"}
|
package/dist/dev/development.js
CHANGED
|
@@ -10,7 +10,7 @@ export async function createDevelopmentHandler({ routes, vite, gracileConfig, })
|
|
|
10
10
|
logger.info('');
|
|
11
11
|
logger.info(c.dim('Creating the request handler…'), { timestamp: true });
|
|
12
12
|
const collectAndCodegen = async () => {
|
|
13
|
-
await collectRoutes(routes, root, gracileConfig.routes?.exclude);
|
|
13
|
+
await collectRoutes(routes, root, gracileConfig.routes?.exclude, gracileConfig.trailingSlash);
|
|
14
14
|
if (gracileConfig.experimental?.generateRoutesTypings)
|
|
15
15
|
await generateRoutesTypings(root, routes).catch((error) => logger.error(String(error)));
|
|
16
16
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-template.d.ts","sourceRoot":"","sources":["../../src/render/route-template.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAIvC,OAAO,EAA0B,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"route-template.d.ts","sourceRoot":"","sources":["../../src/render/route-template.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAIvC,OAAO,EAA0B,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAOxE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAO1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,KAAK,CAAC,MAAM,oBAAoB,CAAC;AAa7C,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAEhF,wBAAsB,mBAAmB,CAAC,EACzC,GAAG,EACH,IAAI,EACJ,IAAI,EACJ,UAAU,EACV,WAAW,EACX,UAAU,EACV,OAAO,EACP,UAAU,GACV,EAAE;IACF,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IACjC,IAAI,EAAE,KAAK,GAAG,OAAO,CAAC;IACtB,UAAU,EAAE,UAAU,CAAC;IACvB,WAAW,CAAC,EAAE,CAAC,CAAC,YAAY,GAAG,SAAS,CAAC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,UAAU,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,SAAS,CAAC;CAC7C,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,IAAI,GAAG,QAAQ,CAAC;IAAC,QAAQ,EAAE,IAAI,GAAG,MAAM,CAAA;CAAE,CAAC,CA4JhE"}
|
|
@@ -32,6 +32,9 @@ export async function renderRouteTemplate({ url, vite, mode, routeInfos, routeAs
|
|
|
32
32
|
});
|
|
33
33
|
const fragmentRender = renderLitSsr(fragmentOutput, mergedRenderInfo);
|
|
34
34
|
const output = Readable.from(fragmentRender);
|
|
35
|
+
// TODO: Disabled for now. Causes issue in static renders.
|
|
36
|
+
// Needs investigations.
|
|
37
|
+
// const output = new RenderResultReadable(fragmentRender);
|
|
35
38
|
return { output, document: null };
|
|
36
39
|
}
|
|
37
40
|
// MARK: Document
|
|
@@ -87,7 +90,8 @@ export async function renderRouteTemplate({ url, vite, mode, routeInfos, routeAs
|
|
|
87
90
|
const routeOutput = await Promise.resolve(routeInfos.routeModule.template(context));
|
|
88
91
|
if (assert.isLitTemplate(routeOutput) === false)
|
|
89
92
|
throw new Error(`Wrong template result for page template ${routeInfos.foundRoute.filePath}.`);
|
|
90
|
-
const renderStream =
|
|
93
|
+
const renderStream =
|
|
94
|
+
/* TODO: Use `new RenderResultReadable` */ Readable.from(renderLitSsr(routeOutput, mergedRenderInfo));
|
|
91
95
|
const output = Readable.from(concatStreams(baseDocumentRenderStreamPre, renderStream, baseDocumentRenderStreamPost));
|
|
92
96
|
return { output, document: baseDocumentHtml };
|
|
93
97
|
}
|
package/dist/routes/collect.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type * as R from './route.js';
|
|
2
2
|
/** @internal Exported for unit testing. */
|
|
3
|
-
export declare function extractRoutePatterns(routeFilePath: string): Pick<R.Route, 'pattern' | 'hasParams'> & {
|
|
3
|
+
export declare function extractRoutePatterns(routeFilePath: string, trailingSlash?: 'always' | 'never' | 'ignore'): Pick<R.Route, 'pattern' | 'hasParams'> & {
|
|
4
4
|
patternString: string;
|
|
5
5
|
};
|
|
6
6
|
export declare const WATCHED_FILES_REGEX: RegExp;
|
|
7
|
-
export declare function collectRoutes(routes: R.RoutesManifest, root: string, excludePatterns?: string[]): Promise<void>;
|
|
7
|
+
export declare function collectRoutes(routes: R.RoutesManifest, root: string, excludePatterns?: string[], trailingSlash?: 'always' | 'never' | 'ignore'): Promise<void>;
|
|
8
8
|
//# sourceMappingURL=collect.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../../src/routes/collect.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,KAAK,CAAC,MAAM,YAAY,CAAC;AAIrC,2CAA2C;AAC3C,wBAAgB,oBAAoB,CACnC,aAAa,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../../src/routes/collect.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,KAAK,CAAC,MAAM,YAAY,CAAC;AAIrC,2CAA2C;AAC3C,wBAAgB,oBAAoB,CACnC,aAAa,EAAE,MAAM,EACrB,aAAa,GAAE,QAAQ,GAAG,OAAO,GAAG,QAAmB,GACrD,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,SAAS,GAAG,WAAW,CAAC,GAAG;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,CAmDpE;AAED,eAAO,MAAM,mBAAmB,QAC4C,CAAC;AAE7E,wBAAsB,aAAa,CAClC,MAAM,EAAE,CAAC,CAAC,cAAc,EACxB,IAAI,EAAE,MAAM,EACZ,eAAe,GAAE,MAAM,EAAO,EAC9B,aAAa,GAAE,QAAQ,GAAG,OAAO,GAAG,QAAmB,GACrD,OAAO,CAAC,IAAI,CAAC,CAiGf"}
|
package/dist/routes/collect.js
CHANGED
|
@@ -5,7 +5,7 @@ import { fdir as Fdir } from 'fdir';
|
|
|
5
5
|
import c from 'picocolors';
|
|
6
6
|
// eslint-disable-next-line import-x/order
|
|
7
7
|
import { URLPattern as URLPatternPolyfill } from 'urlpattern-polyfill/urlpattern';
|
|
8
|
-
//
|
|
8
|
+
// HACK: The polyfill type lacks `hasRegExpGroups` from the global URLPattern.
|
|
9
9
|
const URLPattern = URLPatternPolyfill;
|
|
10
10
|
import { createFilter } from 'vite';
|
|
11
11
|
import { emptyRoutes } from '../logging/messages.js';
|
|
@@ -13,7 +13,7 @@ import { prepareSortableRoutes, routeComparator } from './comparator.js';
|
|
|
13
13
|
import { REGEXES } from './load-module.js';
|
|
14
14
|
const logger = getLogger();
|
|
15
15
|
/** @internal Exported for unit testing. */
|
|
16
|
-
export function extractRoutePatterns(routeFilePath) {
|
|
16
|
+
export function extractRoutePatterns(routeFilePath, trailingSlash = 'ignore') {
|
|
17
17
|
const routePathname = routeFilePath.replace(/\.(js|ts|jsx|tsx|html)$/, '');
|
|
18
18
|
let pathParts = routePathname.split(paths.isWindows() ? paths.WINDOWS_PATH_SEPARATOR : '/');
|
|
19
19
|
const last = pathParts.at(-1);
|
|
@@ -42,8 +42,9 @@ export function extractRoutePatterns(routeFilePath) {
|
|
|
42
42
|
}
|
|
43
43
|
return entry;
|
|
44
44
|
});
|
|
45
|
-
const
|
|
46
|
-
const
|
|
45
|
+
const isRoot = pathRelativeNormalized.length === 0;
|
|
46
|
+
const slash = isRoot || trailingSlash === 'never' ? '' : '/';
|
|
47
|
+
const normalizedUrlPattern = `/${pathRelativeNormalized.join('/')}${slash}`;
|
|
47
48
|
return {
|
|
48
49
|
patternString: normalizedUrlPattern,
|
|
49
50
|
pattern: new URLPattern(normalizedUrlPattern, 'http://gracile/'),
|
|
@@ -51,7 +52,7 @@ export function extractRoutePatterns(routeFilePath) {
|
|
|
51
52
|
};
|
|
52
53
|
}
|
|
53
54
|
export const WATCHED_FILES_REGEX = /\/src\/routes\/(.*)\.(js|ts|jsx|tsx|html|css|scss|sass|less|styl|stylus)$/;
|
|
54
|
-
export async function collectRoutes(routes, root, excludePatterns = []) {
|
|
55
|
+
export async function collectRoutes(routes, root, excludePatterns = [], trailingSlash = 'ignore') {
|
|
55
56
|
routes.clear();
|
|
56
57
|
const routesFolder = 'src/routes';
|
|
57
58
|
const routesFolderAbsolute = join(root, routesFolder);
|
|
@@ -105,7 +106,7 @@ export async function collectRoutes(routes, root, excludePatterns = []) {
|
|
|
105
106
|
// MARK: Associate
|
|
106
107
|
for (const routePath of serverEntrypointsSorted) {
|
|
107
108
|
const filePath = join(routesFolder, routePath);
|
|
108
|
-
const routeWithPatterns = extractRoutePatterns(routePath);
|
|
109
|
+
const routeWithPatterns = extractRoutePatterns(routePath, trailingSlash);
|
|
109
110
|
routes.set(routeWithPatterns.patternString, {
|
|
110
111
|
filePath,
|
|
111
112
|
pattern: routeWithPatterns.pattern,
|
package/dist/routes/match.d.ts
CHANGED
|
@@ -7,8 +7,11 @@ type MatchedRoute = {
|
|
|
7
7
|
params: Parameters_;
|
|
8
8
|
pathname: string;
|
|
9
9
|
};
|
|
10
|
+
export type TrailingSlashRedirect = {
|
|
11
|
+
redirect: string;
|
|
12
|
+
};
|
|
10
13
|
/** @internal Exported for unit testing. */
|
|
11
|
-
export declare function matchRouteFromUrl(url: string, routes: R.RoutesManifest): MatchedRoute | null;
|
|
14
|
+
export declare function matchRouteFromUrl(url: string, routes: R.RoutesManifest, trailingSlash?: 'always' | 'never' | 'ignore'): MatchedRoute | TrailingSlashRedirect | null;
|
|
12
15
|
type ExtractedStaticPaths = {
|
|
13
16
|
staticPaths: R.StaticPathOptionsGeneric[];
|
|
14
17
|
props: unknown;
|
|
@@ -39,6 +42,7 @@ export declare function getRoute(options: {
|
|
|
39
42
|
vite?: ViteDevServer | undefined;
|
|
40
43
|
routes: R.RoutesManifest;
|
|
41
44
|
routeImports?: R.RoutesImports | undefined;
|
|
42
|
-
|
|
45
|
+
trailingSlash?: 'always' | 'never' | 'ignore';
|
|
46
|
+
}): Promise<RouteInfos | TrailingSlashRedirect | null>;
|
|
43
47
|
export {};
|
|
44
48
|
//# sourceMappingURL=match.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"match.d.ts","sourceRoot":"","sources":["../../src/routes/match.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAG1C,OAAO,KAAK,KAAK,CAAC,MAAM,YAAY,CAAC;AAErC,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAEtD,KAAK,YAAY,GAAG;IACnB,KAAK,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACpC,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC;IACpB,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,2CAA2C;AAC3C,wBAAgB,iBAAiB,CAChC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,CAAC,CAAC,cAAc,
|
|
1
|
+
{"version":3,"file":"match.d.ts","sourceRoot":"","sources":["../../src/routes/match.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAG1C,OAAO,KAAK,KAAK,CAAC,MAAM,YAAY,CAAC;AAErC,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAEtD,KAAK,YAAY,GAAG;IACnB,KAAK,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACpC,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC;IACpB,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzD,2CAA2C;AAC3C,wBAAgB,iBAAiB,CAChC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,CAAC,CAAC,cAAc,EACxB,aAAa,GAAE,QAAQ,GAAG,OAAO,GAAG,QAAmB,GACrD,YAAY,GAAG,qBAAqB,GAAG,IAAI,CAyC7C;AAED,KAAK,oBAAoB,GAAG;IAC3B,WAAW,EAAE,CAAC,CAAC,wBAAwB,EAAE,CAAC;IAC1C,KAAK,EAAE,OAAO,CAAC;CACf,GAAG,IAAI,CAAC;AACT;;;;;;GAMG;AACH,2CAA2C;AAC3C,wBAAsB,kBAAkB,CAAC,OAAO,EAAE;IACjD,WAAW,EAAE,CAAC,CAAC,WAAW,CAAC;IAC3B,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC;IACpB,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA6BhC;AAED,MAAM,MAAM,UAAU,GAAG;IACxB,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IACrC,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAsB,QAAQ,CAAC,OAAO,EAAE;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IACjC,MAAM,EAAE,CAAC,CAAC,cAAc,CAAC;IACzB,YAAY,CAAC,EAAE,CAAC,CAAC,aAAa,GAAG,SAAS,CAAC;IAC3C,aAAa,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;CAC9C,GAAG,OAAO,CAAC,UAAU,GAAG,qBAAqB,GAAG,IAAI,CAAC,CAkCrD"}
|
package/dist/routes/match.js
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
import { loadForeignRouteObject } from './load-module.js';
|
|
2
2
|
/** @internal Exported for unit testing. */
|
|
3
|
-
export function matchRouteFromUrl(url, routes) {
|
|
3
|
+
export function matchRouteFromUrl(url, routes, trailingSlash = 'ignore') {
|
|
4
4
|
let match;
|
|
5
5
|
let foundRoute;
|
|
6
|
-
const
|
|
6
|
+
const rawPathname = new URL(url).pathname;
|
|
7
|
+
// Handle redirect cases for 'always' and 'never' before matching.
|
|
8
|
+
// Root '/' is exempt — it always keeps its slash.
|
|
9
|
+
if (rawPathname !== '/') {
|
|
10
|
+
if (trailingSlash === 'always' && !rawPathname.endsWith('/'))
|
|
11
|
+
return { redirect: rawPathname + '/' };
|
|
12
|
+
if (trailingSlash === 'never' && rawPathname.endsWith('/'))
|
|
13
|
+
return { redirect: rawPathname.slice(0, -1) };
|
|
14
|
+
}
|
|
15
|
+
// For 'ignore', normalize to trailing-slash so it matches stored patterns.
|
|
16
|
+
const pathname = trailingSlash === 'ignore' &&
|
|
17
|
+
rawPathname !== '/' &&
|
|
18
|
+
!rawPathname.endsWith('/')
|
|
19
|
+
? rawPathname + '/'
|
|
20
|
+
: rawPathname;
|
|
7
21
|
for (const [, route] of routes) {
|
|
8
22
|
if (match)
|
|
9
23
|
break;
|
|
@@ -49,8 +63,10 @@ export async function extractStaticPaths(options) {
|
|
|
49
63
|
return { staticPaths, props: properties };
|
|
50
64
|
}
|
|
51
65
|
export async function getRoute(options) {
|
|
52
|
-
const matchedRoute = matchRouteFromUrl(options.url, options.routes);
|
|
66
|
+
const matchedRoute = matchRouteFromUrl(options.url, options.routes, options.trailingSlash);
|
|
53
67
|
if (!matchedRoute)
|
|
68
|
+
return null;
|
|
69
|
+
if ('redirect' in matchedRoute)
|
|
54
70
|
return matchedRoute;
|
|
55
71
|
const { foundRoute, pathname, params } = matchedRoute;
|
|
56
72
|
const routeModule = await loadForeignRouteObject({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/routes/render.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAG1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAIvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,WAAW,uBAAuB;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAEpB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,MAAM,EAAE;QACP,KAAK,EAAE,OAAO,CAAC;QACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;KACxB,CAAC;IAEF,aAAa,EAAE,OAAO,GAAG,IAAI,CAAC;CAC9B;
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/routes/render.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAG1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAIvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,WAAW,uBAAuB;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAEpB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,MAAM,EAAE;QACP,KAAK,EAAE,OAAO,CAAC;QACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;KACxB,CAAC;IAEF,aAAa,EAAE,OAAO,GAAG,IAAI,CAAC;CAC9B;AAwBD,wBAAsB,YAAY,CAAC,EAClC,MAAM,EACN,IAAI,EACJ,UAAU,EACV,IAAoB,EACpB,aAAa,GACb,EAAE;IACF,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,aAAa,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,aAAa,CAAC;CAC7B;;;GAwKA"}
|
package/dist/routes/render.js
CHANGED
|
@@ -8,11 +8,18 @@ import { loadForeignRouteObject } from './load-module.js';
|
|
|
8
8
|
async function streamToString(stream) {
|
|
9
9
|
const chunks = [];
|
|
10
10
|
for await (const chunk of stream) {
|
|
11
|
+
// NOTE: Since using Lit's `RenderResultReadable` instead of pure
|
|
12
|
+
// Node Readable, the chunk can be a string or a Buffer.
|
|
13
|
+
// If it's a string, convert it to Buffer first.
|
|
11
14
|
if (typeof chunk === 'string') {
|
|
12
15
|
chunks.push(Buffer.from(chunk));
|
|
13
16
|
}
|
|
14
17
|
else
|
|
15
|
-
throw new TypeError('Wrong buffer');
|
|
18
|
+
throw new TypeError('Wrong buffer type from stream. Should be a `string` only.');
|
|
19
|
+
// NOTE: Disabled for now. Causes issues with `RenderResultReadable`.
|
|
20
|
+
/* else {
|
|
21
|
+
chunks.push(chunk);
|
|
22
|
+
} */
|
|
16
23
|
}
|
|
17
24
|
return Buffer.concat(chunks).toString('utf8');
|
|
18
25
|
}
|
|
@@ -20,7 +27,7 @@ export async function renderRoutes({ routes, vite, serverMode, root = process.cw
|
|
|
20
27
|
const logger = getLogger();
|
|
21
28
|
logger.info(c.green('Rendering routes…'), { timestamp: true });
|
|
22
29
|
// MARK: Collect
|
|
23
|
-
await collectRoutes(routes, root, gracileConfig.routes?.exclude);
|
|
30
|
+
await collectRoutes(routes, root, gracileConfig.routes?.exclude, gracileConfig.trailingSlash);
|
|
24
31
|
const renderedRoutes = [];
|
|
25
32
|
// MARK: Iterate modules
|
|
26
33
|
await Promise.all([...routes].map(async ([patternString, route]) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../src/server/request.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAOlD,OAAO,KAAK,KAAK,CAAC,MAAM,oBAAoB,CAAC;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,EACN,KAAK,aAAa,EAOlB,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,cAAc;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,CAC5B,OAAO,EAAE,OAAO,EAChB,MAAM,CAAC,EAAE,OAAO,KACZ,OAAO,CAAC,aAAa,CAAC,CAAC;AAE5B,wBAAgB,oBAAoB,CAAC,EACpC,IAAI,EACJ,MAAM,EACN,YAAY,EACZ,WAAW,EACX,IAAI,EACJ,UAAU,EACV,aAAa,GACb,EAAE;IACF,IAAI,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IACjC,MAAM,EAAE,CAAC,CAAC,cAAc,CAAC;IACzB,YAAY,CAAC,EAAE,CAAC,CAAC,aAAa,GAAG,SAAS,CAAC;IAC3C,WAAW,CAAC,EAAE,CAAC,CAAC,YAAY,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,aAAa,EAAE,aAAa,CAAC;CAC7B,
|
|
1
|
+
{"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../src/server/request.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAOlD,OAAO,KAAK,KAAK,CAAC,MAAM,oBAAoB,CAAC;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,EACN,KAAK,aAAa,EAOlB,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,cAAc;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,CAC5B,OAAO,EAAE,OAAO,EAChB,MAAM,CAAC,EAAE,OAAO,KACZ,OAAO,CAAC,aAAa,CAAC,CAAC;AAE5B,wBAAgB,oBAAoB,CAAC,EACpC,IAAI,EACJ,MAAM,EACN,YAAY,EACZ,WAAW,EACX,IAAI,EACJ,UAAU,EACV,aAAa,GACb,EAAE;IACF,IAAI,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IACjC,MAAM,EAAE,CAAC,CAAC,cAAc,CAAC;IACzB,YAAY,CAAC,EAAE,CAAC,CAAC,aAAa,GAAG,SAAS,CAAC;IAC3C,WAAW,CAAC,EAAE,CAAC,CAAC,YAAY,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,aAAa,EAAE,aAAa,CAAC;CAC7B,kBAuIA;AAyBD,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAC7B,UAAU,GACV,MAAM,uBAAuB,CAAC"}
|
package/dist/server/request.js
CHANGED
|
@@ -25,14 +25,31 @@ export function createGracileHandler({ vite, routes, routeImports, routeAssets,
|
|
|
25
25
|
vite,
|
|
26
26
|
routes,
|
|
27
27
|
routeImports,
|
|
28
|
+
trailingSlash: gracileConfig.trailingSlash ?? 'ignore',
|
|
28
29
|
};
|
|
29
30
|
const responseInit = {};
|
|
30
|
-
|
|
31
|
+
const routeResult = await getRoute(routeOptions);
|
|
32
|
+
// Trailing slash redirect (301 for GET, 308 for other methods)
|
|
33
|
+
if (routeResult && 'redirect' in routeResult) {
|
|
34
|
+
const redirectUrl = new URL(routeResult.redirect, fullUrl).href;
|
|
35
|
+
const status = method === 'GET' ? 301 : 308;
|
|
36
|
+
return { response: Response.redirect(redirectUrl, status) };
|
|
37
|
+
}
|
|
38
|
+
let routeInfos = routeResult;
|
|
31
39
|
if (routeInfos === null) {
|
|
32
40
|
responseInit.status = 404;
|
|
41
|
+
// Use 'ignore' for the internal 404 lookup to avoid redirect loops.
|
|
33
42
|
const url = new URL('/404/', fullUrl).href;
|
|
34
|
-
const options = {
|
|
35
|
-
|
|
43
|
+
const options = {
|
|
44
|
+
...routeOptions,
|
|
45
|
+
url,
|
|
46
|
+
trailingSlash: 'ignore',
|
|
47
|
+
};
|
|
48
|
+
const notFoundResult = await getRoute(options);
|
|
49
|
+
routeInfos =
|
|
50
|
+
notFoundResult && !('redirect' in notFoundResult)
|
|
51
|
+
? notFoundResult
|
|
52
|
+
: null;
|
|
36
53
|
}
|
|
37
54
|
if (routeInfos === null) {
|
|
38
55
|
const page = builtIn404Page(new URL(fullUrl).pathname, Boolean(vite));
|
package/dist/user-config.d.ts
CHANGED
|
@@ -38,6 +38,19 @@ export interface GracileConfig {
|
|
|
38
38
|
* @defaultValue 'static'
|
|
39
39
|
*/
|
|
40
40
|
output?: 'static' | 'server';
|
|
41
|
+
/**
|
|
42
|
+
* Controls how trailing slashes are matched on incoming URLs.
|
|
43
|
+
*
|
|
44
|
+
* - `'ignore'` — Match regardless of whether a trailing `/` is present.
|
|
45
|
+
* `/about` and `/about/` both resolve to the same route. *(default)*
|
|
46
|
+
* - `'always'` — Only match URLs that include a trailing slash (e.g. `/about/`).
|
|
47
|
+
* Requests without one are redirected: `301` for GET, `308` for other methods.
|
|
48
|
+
* - `'never'` — Only match URLs that do not include a trailing slash (e.g. `/about`).
|
|
49
|
+
* Requests with one are redirected: `301` for GET, `308` for other methods.
|
|
50
|
+
*
|
|
51
|
+
* @defaultValue 'ignore'
|
|
52
|
+
*/
|
|
53
|
+
trailingSlash?: 'always' | 'never' | 'ignore';
|
|
41
54
|
/**
|
|
42
55
|
* Settings for the development mode.
|
|
43
56
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-config.d.ts","sourceRoot":"","sources":["../src/user-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,WAAW,aAAa;IAC7B;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAE7B;;OAEG;IACH,GAAG,CAAC,EAAE;QACL;;;;;WAKG;QACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE;YAAE,WAAW,EAAE,OAAO,CAAC,eAAe,CAAA;SAAE,KAAK,OAAO,CAAC;KACxE,CAAC;IAEF;;OAEG;IACH,MAAM,CAAC,EAAE;QACR;;WAEG;QACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;KACnB,CAAC;IAEF;;OAEG;IACH,KAAK,CAAC,EAAE;QACP;;;;;;;;;;;;;WAaG;QACH,QAAQ,CAAC,EAAE;YACV;;eAEG;YACH,MAAM,CAAC,EAAE,OAAO,CAAC;YAGjB;;eAEG;YACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;YAEnB;;eAEG;YACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;SACnB,CAAC;KACF,CAAC;IACF,MAAM,CAAC,EAAE;QACR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WA8BG;QACH,UAAU,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;KACjC,CAAC;IAEF;;OAEG;IACH,YAAY,CAAC,EAAE;QACd;;;WAGG;QACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;KAOhC,CAAC;CACF"}
|
|
1
|
+
{"version":3,"file":"user-config.d.ts","sourceRoot":"","sources":["../src/user-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,WAAW,aAAa;IAC7B;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAE7B;;;;;;;;;;;OAWG;IACH,aAAa,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;IAE9C;;OAEG;IACH,GAAG,CAAC,EAAE;QACL;;;;;WAKG;QACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE;YAAE,WAAW,EAAE,OAAO,CAAC,eAAe,CAAA;SAAE,KAAK,OAAO,CAAC;KACxE,CAAC;IAEF;;OAEG;IACH,MAAM,CAAC,EAAE;QACR;;WAEG;QACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;KACnB,CAAC;IAEF;;OAEG;IACH,KAAK,CAAC,EAAE;QACP;;;;;;;;;;;;;WAaG;QACH,QAAQ,CAAC,EAAE;YACV;;eAEG;YACH,MAAM,CAAC,EAAE,OAAO,CAAC;YAGjB;;eAEG;YACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;YAEnB;;eAEG;YACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;SACnB,CAAC;KACF,CAAC;IACF,MAAM,CAAC,EAAE;QACR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WA8BG;QACH,UAAU,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;KACjC,CAAC;IAEF;;OAEG;IACH,YAAY,CAAC,EAAE;QACd;;;WAGG;QACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;KAOhC,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gracile/engine",
|
|
3
|
-
"version": "0.9.0-next.
|
|
3
|
+
"version": "0.9.0-next.6",
|
|
4
4
|
"description": "A thin, full-stack, web framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"custom-elements",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"!/dist/typedoc-entrypoint.*"
|
|
44
44
|
],
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@gracile-labs/better-errors": "^0.2.
|
|
47
|
-
"@gracile/internal-utils": "^0.6.
|
|
46
|
+
"@gracile-labs/better-errors": "^0.2.1-next.0",
|
|
47
|
+
"@gracile/internal-utils": "^0.6.1-next.0",
|
|
48
48
|
"@whatwg-node/server": "^0.10.18",
|
|
49
49
|
"fdir": "^6.5.0",
|
|
50
50
|
"picocolors": "^1.1.1",
|
|
@@ -60,5 +60,5 @@
|
|
|
60
60
|
"access": "public",
|
|
61
61
|
"provenance": true
|
|
62
62
|
},
|
|
63
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "a83e253566cdeb19aa75c7d0a686e3e52e921eed"
|
|
64
64
|
}
|