@gracile/engine 0.9.0-next.4 → 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/plugin.d.ts.map +1 -1
- package/dist/plugin.js +37 -262
- package/dist/render/route-template-pipeline.d.ts +64 -0
- package/dist/render/route-template-pipeline.d.ts.map +1 -0
- package/dist/render/route-template-pipeline.js +144 -0
- package/dist/render/route-template.d.ts +1 -2
- package/dist/render/route-template.d.ts.map +1 -1
- package/dist/render/route-template.js +17 -93
- package/dist/routes/collect.d.ts +5 -1
- package/dist/routes/collect.d.ts.map +1 -1
- package/dist/routes/collect.js +8 -6
- package/dist/routes/match.d.ts +31 -1
- package/dist/routes/match.d.ts.map +1 -1
- package/dist/routes/match.js +22 -4
- package/dist/routes/render.d.ts.map +1 -1
- package/dist/routes/render.js +9 -2
- package/dist/server/request-pipeline.d.ts +109 -0
- package/dist/server/request-pipeline.d.ts.map +1 -0
- package/dist/server/request-pipeline.js +198 -0
- package/dist/server/request.d.ts +3 -16
- package/dist/server/request.d.ts.map +1 -1
- package/dist/server/request.js +74 -171
- package/dist/test/init.d.ts +2 -0
- package/dist/test/init.d.ts.map +1 -0
- package/dist/test/init.js +7 -0
- package/dist/user-config.d.ts +13 -0
- package/dist/user-config.d.ts.map +1 -1
- package/dist/vite/plugin-client-build.d.ts +16 -0
- package/dist/vite/plugin-client-build.d.ts.map +1 -0
- package/dist/vite/plugin-client-build.js +49 -0
- package/dist/vite/plugin-serve.d.ts +18 -0
- package/dist/vite/plugin-serve.d.ts.map +1 -0
- package/dist/vite/plugin-serve.js +62 -0
- package/dist/vite/plugin-server-build.d.ts +33 -0
- package/dist/vite/plugin-server-build.d.ts.map +1 -0
- package/dist/vite/plugin-server-build.js +157 -0
- package/dist/vite/plugin-shared-state.d.ts +31 -0
- package/dist/vite/plugin-shared-state.d.ts.map +1 -0
- package/dist/vite/plugin-shared-state.js +22 -0
- package/package.json +4 -4
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
import { Readable } from 'node:stream';
|
|
2
2
|
import * as assert from '@gracile/internal-utils/assertions';
|
|
3
|
-
import { html } from '@gracile/internal-utils/dummy-literals';
|
|
4
3
|
import { render as renderLitSsr } from '@lit-labs/ssr';
|
|
5
4
|
import { collectResult } from '@lit-labs/ssr/lib/render-result.js';
|
|
6
|
-
import { LitElementRenderer } from '@lit-labs/ssr/lib/lit-element-renderer.js';
|
|
7
5
|
import { GracileError, GracileErrorData, TemplateError, } from '../errors/errors.js';
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
yield chunk;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
export const REGEX_TAG_SCRIPT = /\s?<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script\s*>\s?/gi;
|
|
17
|
-
export const REGEX_TAG_LINK = /\s?<link\b[^>]*?>\s?/gi;
|
|
6
|
+
import { SSR_OUTLET_MARKER } from './markers.js';
|
|
7
|
+
import { concatStreams, mergeRenderInfo, injectSiblingAssets, ensureDoctype, injectDevelopmentOverlay, injectServerAssets, } from './route-template-pipeline.js';
|
|
8
|
+
// Re-export for consumers that import these regexes from this module.
|
|
9
|
+
export { REGEX_TAG_SCRIPT, REGEX_TAG_LINK } from './route-template-pipeline.js';
|
|
18
10
|
export async function renderRouteTemplate({ url, vite, mode, routeInfos, routeAssets, serverMode, docOnly, renderInfo, }) {
|
|
19
11
|
const location = {
|
|
20
12
|
file: routeInfos.foundRoute.filePath,
|
|
@@ -22,13 +14,7 @@ export async function renderRouteTemplate({ url, vite, mode, routeInfos, routeAs
|
|
|
22
14
|
if (!routeInfos.routeModule.document && !routeInfos.routeModule.template)
|
|
23
15
|
return { output: null, document: null };
|
|
24
16
|
// MARK: Merged render info
|
|
25
|
-
const mergedRenderInfo =
|
|
26
|
-
...renderInfo,
|
|
27
|
-
elementRenderers: [
|
|
28
|
-
...(renderInfo?.elementRenderers || []),
|
|
29
|
-
LitElementRenderer,
|
|
30
|
-
],
|
|
31
|
-
};
|
|
17
|
+
const mergedRenderInfo = mergeRenderInfo(renderInfo);
|
|
32
18
|
// MARK: Context
|
|
33
19
|
const context = {
|
|
34
20
|
url: new URL(url),
|
|
@@ -46,6 +32,9 @@ export async function renderRouteTemplate({ url, vite, mode, routeInfos, routeAs
|
|
|
46
32
|
});
|
|
47
33
|
const fragmentRender = renderLitSsr(fragmentOutput, mergedRenderInfo);
|
|
48
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);
|
|
49
38
|
return { output, document: null };
|
|
50
39
|
}
|
|
51
40
|
// MARK: Document
|
|
@@ -74,76 +63,17 @@ export async function renderRouteTemplate({ url, vite, mode, routeInfos, routeAs
|
|
|
74
63
|
location,
|
|
75
64
|
}, { cause: String(error) });
|
|
76
65
|
}
|
|
77
|
-
// MARK:
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
baseDocumentRendered = baseDocumentRendered
|
|
81
|
-
.replace('</head>', `\n${PAGE_ASSETS_MARKER}</head>`)
|
|
82
|
-
.replace(PAGE_ASSETS_MARKER, routeInfos.foundRoute.pageAssets.length > 0
|
|
83
|
-
? html `<!-- PAGE ASSETS -->` +
|
|
84
|
-
`${routeInfos.foundRoute.pageAssets
|
|
85
|
-
.map((path) => {
|
|
86
|
-
//
|
|
87
|
-
if (/\.(js|ts|jsx|tsx)$/.test(path)) {
|
|
88
|
-
// prettier-ignore
|
|
89
|
-
return html ` <script type="module" src="/${path}"></script>`;
|
|
90
|
-
}
|
|
91
|
-
if (/\.(css|scss|sass|less|styl|stylus)$/.test(path)) {
|
|
92
|
-
// prettier-ignore
|
|
93
|
-
return html ` <link rel="stylesheet" href="/${path}" />`;
|
|
94
|
-
}
|
|
95
|
-
// NOTE: Never called (filtered upstream in `collectRoutes`)
|
|
96
|
-
return null;
|
|
97
|
-
})
|
|
98
|
-
.join('\n')}` +
|
|
99
|
-
`<!-- /PAGE ASSETS -->\n `
|
|
100
|
-
: '');
|
|
101
|
-
// MARK: Add doctype if missing.
|
|
102
|
-
if (baseDocumentRendered
|
|
103
|
-
.trimStart()
|
|
104
|
-
.toLocaleLowerCase()
|
|
105
|
-
.startsWith('<!doctype') === false)
|
|
106
|
-
baseDocumentRendered = `<!doctype html>\n${baseDocumentRendered}`;
|
|
107
|
-
// MARK: Dev. overlay.
|
|
108
|
-
// TODO: Need more testing and refinement (refreshes kills its usefulness).
|
|
109
|
-
const overlay = () => html `
|
|
110
|
-
<script type="module">
|
|
111
|
-
if (import.meta.hot) {
|
|
112
|
-
import.meta.hot.on('gracile:ssr-error', (error) => {
|
|
113
|
-
console.error(error.message);
|
|
114
|
-
});
|
|
115
|
-
import.meta.hot.on('error', (payload) => {
|
|
116
|
-
console.error(payload.err.message);
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
</script>
|
|
120
|
-
`;
|
|
66
|
+
// MARK: Post-process document HTML (pure transforms)
|
|
67
|
+
baseDocumentRendered = injectSiblingAssets(baseDocumentRendered, routeInfos.foundRoute.pageAssets);
|
|
68
|
+
baseDocumentRendered = ensureDoctype(baseDocumentRendered);
|
|
121
69
|
if (mode === 'dev')
|
|
122
|
-
baseDocumentRendered = baseDocumentRendered
|
|
123
|
-
// MARK: Inject assets for server output runtime only.
|
|
70
|
+
baseDocumentRendered = injectDevelopmentOverlay(baseDocumentRendered);
|
|
124
71
|
const routeAssetsString = routeAssets?.get?.(routeInfos.foundRoute.pattern.pathname);
|
|
125
72
|
if (routeAssetsString)
|
|
126
|
-
baseDocumentRendered = baseDocumentRendered
|
|
127
|
-
|
|
128
|
-
if (s.includes(`type="module"`))
|
|
129
|
-
return '';
|
|
130
|
-
return s;
|
|
131
|
-
})
|
|
132
|
-
.replaceAll(REGEX_TAG_LINK, (s) => {
|
|
133
|
-
if (s.includes(`rel="stylesheet"`))
|
|
134
|
-
return '';
|
|
135
|
-
return s;
|
|
136
|
-
})
|
|
137
|
-
.replace('</head>', `${routeAssetsString}\n</head>`);
|
|
138
|
-
// MARK: Base document
|
|
73
|
+
baseDocumentRendered = injectServerAssets(baseDocumentRendered, routeAssetsString);
|
|
74
|
+
// MARK: Base document (Vite HTML transform in dev)
|
|
139
75
|
const baseDocumentHtml = vite && mode === 'dev'
|
|
140
|
-
? await vite.transformIndexHtml(
|
|
141
|
-
// HACK: Sometimes, we need to invalidate for server asset url
|
|
142
|
-
// imports to work. So we keep this hack around just in case.
|
|
143
|
-
// Maybe it's linked to the way hashed assets are invalidating
|
|
144
|
-
// the html proxy module…
|
|
145
|
-
// `${routeInfos.pathname}?r=${Math.random()}`,
|
|
146
|
-
routeInfos.pathname, baseDocumentRendered)
|
|
76
|
+
? await vite.transformIndexHtml(routeInfos.pathname, baseDocumentRendered)
|
|
147
77
|
: baseDocumentRendered;
|
|
148
78
|
if (docOnly)
|
|
149
79
|
return { document: baseDocumentHtml, output: null };
|
|
@@ -158,16 +88,10 @@ export async function renderRouteTemplate({ url, vite, mode, routeInfos, routeAs
|
|
|
158
88
|
(serverMode &&
|
|
159
89
|
(mode !== 'build' || routeInfos.routeModule.prerender === true)))) {
|
|
160
90
|
const routeOutput = await Promise.resolve(routeInfos.routeModule.template(context));
|
|
161
|
-
// NOTE: Explicitely unset template (maybe a bad idea as a feature. We'll see)
|
|
162
|
-
// if (routeOutput === null || routeOutput === undefined) {
|
|
163
|
-
// const output = Readable.from(
|
|
164
|
-
// concatStreams(baseDocRenderStreamPre, baseDocRenderStreamPost),
|
|
165
|
-
// );
|
|
166
|
-
// return { output, document: null };
|
|
167
|
-
// }
|
|
168
91
|
if (assert.isLitTemplate(routeOutput) === false)
|
|
169
92
|
throw new Error(`Wrong template result for page template ${routeInfos.foundRoute.filePath}.`);
|
|
170
|
-
const renderStream =
|
|
93
|
+
const renderStream =
|
|
94
|
+
/* TODO: Use `new RenderResultReadable` */ Readable.from(renderLitSsr(routeOutput, mergedRenderInfo));
|
|
171
95
|
const output = Readable.from(concatStreams(baseDocumentRenderStreamPre, renderStream, baseDocumentRenderStreamPost));
|
|
172
96
|
return { output, document: baseDocumentHtml };
|
|
173
97
|
}
|
package/dist/routes/collect.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type * as R from './route.js';
|
|
2
|
+
/** @internal Exported for unit testing. */
|
|
3
|
+
export declare function extractRoutePatterns(routeFilePath: string, trailingSlash?: 'always' | 'never' | 'ignore'): Pick<R.Route, 'pattern' | 'hasParams'> & {
|
|
4
|
+
patternString: string;
|
|
5
|
+
};
|
|
2
6
|
export declare const WATCHED_FILES_REGEX: RegExp;
|
|
3
|
-
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>;
|
|
4
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;
|
|
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,14 +5,15 @@ 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';
|
|
12
12
|
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, trailingSlash = 'ignore') {
|
|
16
17
|
const routePathname = routeFilePath.replace(/\.(js|ts|jsx|tsx|html)$/, '');
|
|
17
18
|
let pathParts = routePathname.split(paths.isWindows() ? paths.WINDOWS_PATH_SEPARATOR : '/');
|
|
18
19
|
const last = pathParts.at(-1);
|
|
@@ -41,8 +42,9 @@ function extractRoutePatterns(routeFilePath) {
|
|
|
41
42
|
}
|
|
42
43
|
return entry;
|
|
43
44
|
});
|
|
44
|
-
const
|
|
45
|
-
const
|
|
45
|
+
const isRoot = pathRelativeNormalized.length === 0;
|
|
46
|
+
const slash = isRoot || trailingSlash === 'never' ? '' : '/';
|
|
47
|
+
const normalizedUrlPattern = `/${pathRelativeNormalized.join('/')}${slash}`;
|
|
46
48
|
return {
|
|
47
49
|
patternString: normalizedUrlPattern,
|
|
48
50
|
pattern: new URLPattern(normalizedUrlPattern, 'http://gracile/'),
|
|
@@ -50,7 +52,7 @@ function extractRoutePatterns(routeFilePath) {
|
|
|
50
52
|
};
|
|
51
53
|
}
|
|
52
54
|
export const WATCHED_FILES_REGEX = /\/src\/routes\/(.*)\.(js|ts|jsx|tsx|html|css|scss|sass|less|styl|stylus)$/;
|
|
53
|
-
export async function collectRoutes(routes, root, excludePatterns = []) {
|
|
55
|
+
export async function collectRoutes(routes, root, excludePatterns = [], trailingSlash = 'ignore') {
|
|
54
56
|
routes.clear();
|
|
55
57
|
const routesFolder = 'src/routes';
|
|
56
58
|
const routesFolderAbsolute = join(root, routesFolder);
|
|
@@ -104,7 +106,7 @@ export async function collectRoutes(routes, root, excludePatterns = []) {
|
|
|
104
106
|
// MARK: Associate
|
|
105
107
|
for (const routePath of serverEntrypointsSorted) {
|
|
106
108
|
const filePath = join(routesFolder, routePath);
|
|
107
|
-
const routeWithPatterns = extractRoutePatterns(routePath);
|
|
109
|
+
const routeWithPatterns = extractRoutePatterns(routePath, trailingSlash);
|
|
108
110
|
routes.set(routeWithPatterns.patternString, {
|
|
109
111
|
filePath,
|
|
110
112
|
pattern: routeWithPatterns.pattern,
|
package/dist/routes/match.d.ts
CHANGED
|
@@ -1,6 +1,35 @@
|
|
|
1
1
|
import type { ViteDevServer } from 'vite';
|
|
2
2
|
import type * as R from './route.js';
|
|
3
3
|
type Parameters_ = Record<string, string | undefined>;
|
|
4
|
+
type MatchedRoute = {
|
|
5
|
+
match: URLPatternResult | undefined;
|
|
6
|
+
foundRoute: R.Route;
|
|
7
|
+
params: Parameters_;
|
|
8
|
+
pathname: string;
|
|
9
|
+
};
|
|
10
|
+
export type TrailingSlashRedirect = {
|
|
11
|
+
redirect: string;
|
|
12
|
+
};
|
|
13
|
+
/** @internal Exported for unit testing. */
|
|
14
|
+
export declare function matchRouteFromUrl(url: string, routes: R.RoutesManifest, trailingSlash?: 'always' | 'never' | 'ignore'): MatchedRoute | TrailingSlashRedirect | null;
|
|
15
|
+
type ExtractedStaticPaths = {
|
|
16
|
+
staticPaths: R.StaticPathOptionsGeneric[];
|
|
17
|
+
props: unknown;
|
|
18
|
+
} | null;
|
|
19
|
+
/**
|
|
20
|
+
* @param options
|
|
21
|
+
* @param options.routeModule
|
|
22
|
+
* @param options.foundRoute
|
|
23
|
+
* @param options.params
|
|
24
|
+
* @param options.pathname
|
|
25
|
+
*/
|
|
26
|
+
/** @internal Exported for unit testing. */
|
|
27
|
+
export declare function extractStaticPaths(options: {
|
|
28
|
+
routeModule: R.RouteModule;
|
|
29
|
+
foundRoute: R.Route;
|
|
30
|
+
params: Parameters_;
|
|
31
|
+
pathname: string;
|
|
32
|
+
}): Promise<ExtractedStaticPaths>;
|
|
4
33
|
export type RouteInfos = {
|
|
5
34
|
params: Parameters_;
|
|
6
35
|
props: unknown;
|
|
@@ -13,6 +42,7 @@ export declare function getRoute(options: {
|
|
|
13
42
|
vite?: ViteDevServer | undefined;
|
|
14
43
|
routes: R.RoutesManifest;
|
|
15
44
|
routeImports?: R.RoutesImports | undefined;
|
|
16
|
-
|
|
45
|
+
trailingSlash?: 'always' | 'never' | 'ignore';
|
|
46
|
+
}): Promise<RouteInfos | TrailingSlashRedirect | null>;
|
|
17
47
|
export {};
|
|
18
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;
|
|
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,8 +1,23 @@
|
|
|
1
1
|
import { loadForeignRouteObject } from './load-module.js';
|
|
2
|
-
|
|
2
|
+
/** @internal Exported for unit testing. */
|
|
3
|
+
export function matchRouteFromUrl(url, routes, trailingSlash = 'ignore') {
|
|
3
4
|
let match;
|
|
4
5
|
let foundRoute;
|
|
5
|
-
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;
|
|
6
21
|
for (const [, route] of routes) {
|
|
7
22
|
if (match)
|
|
8
23
|
break;
|
|
@@ -24,7 +39,8 @@ function matchRouteFromUrl(url, routes) {
|
|
|
24
39
|
* @param options.params
|
|
25
40
|
* @param options.pathname
|
|
26
41
|
*/
|
|
27
|
-
|
|
42
|
+
/** @internal Exported for unit testing. */
|
|
43
|
+
export async function extractStaticPaths(options) {
|
|
28
44
|
if (!options.foundRoute.hasParams)
|
|
29
45
|
return null;
|
|
30
46
|
if (!options.routeModule.staticPaths)
|
|
@@ -47,8 +63,10 @@ async function extractStaticPaths(options) {
|
|
|
47
63
|
return { staticPaths, props: properties };
|
|
48
64
|
}
|
|
49
65
|
export async function getRoute(options) {
|
|
50
|
-
const matchedRoute = matchRouteFromUrl(options.url, options.routes);
|
|
66
|
+
const matchedRoute = matchRouteFromUrl(options.url, options.routes, options.trailingSlash);
|
|
51
67
|
if (!matchedRoute)
|
|
68
|
+
return null;
|
|
69
|
+
if ('redirect' in matchedRoute)
|
|
52
70
|
return matchedRoute;
|
|
53
71
|
const { foundRoute, pathname, params } = matchedRoute;
|
|
54
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]) => {
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, testable pipeline steps extracted from the request handler.
|
|
3
|
+
*
|
|
4
|
+
* Each function here is a focused stage of the Gracile request lifecycle.
|
|
5
|
+
* They are composed by `createGracileHandler` in `./request.ts`.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
import { Readable } from 'node:stream';
|
|
10
|
+
import type { Logger, ViteDevServer } from 'vite';
|
|
11
|
+
import { renderRouteTemplate } from '../render/route-template.js';
|
|
12
|
+
import type { RouteInfos } from '../routes/match.js';
|
|
13
|
+
import type { GracileConfig } from '../user-config.js';
|
|
14
|
+
export type StandardResponse = {
|
|
15
|
+
response: Response;
|
|
16
|
+
body?: never;
|
|
17
|
+
init?: never;
|
|
18
|
+
};
|
|
19
|
+
export type ResponseWithNodeReadable = {
|
|
20
|
+
response?: never;
|
|
21
|
+
body: Readable;
|
|
22
|
+
init: ResponseInit;
|
|
23
|
+
};
|
|
24
|
+
export type HandlerResult = StandardResponse | ResponseWithNodeReadable | null;
|
|
25
|
+
export declare const CONTENT_TYPE_HTML: {
|
|
26
|
+
readonly 'Content-Type': "text/html";
|
|
27
|
+
};
|
|
28
|
+
export declare const PREMISE_REGEXES: {
|
|
29
|
+
readonly properties: RegExp;
|
|
30
|
+
readonly document: RegExp;
|
|
31
|
+
};
|
|
32
|
+
/** Describes which premises endpoint was requested, if any. */
|
|
33
|
+
export interface PremisesDescriptor {
|
|
34
|
+
propertiesOnly: boolean;
|
|
35
|
+
documentOnly: boolean;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Strip the `/__…` suffix so hidden-sibling URLs (premises) resolve to
|
|
39
|
+
* the parent route.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* rewriteHiddenRoutes('http://localhost/blog/__index.props.json')
|
|
43
|
+
* // → 'http://localhost/blog/'
|
|
44
|
+
*/
|
|
45
|
+
export declare function rewriteHiddenRoutes(url: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Determine if the incoming request targets a premises endpoint
|
|
48
|
+
* (`__index.props.json` or `__index.doc.html`) and whether the config
|
|
49
|
+
* allows it.
|
|
50
|
+
*
|
|
51
|
+
* @returns A descriptor when premises are enabled, or `null` when not.
|
|
52
|
+
* @throws When a premise URL is hit but premises are not enabled.
|
|
53
|
+
*/
|
|
54
|
+
export declare function resolvePremises(requestedUrl: string, gracileConfig: GracileConfig): PremisesDescriptor | null;
|
|
55
|
+
export interface ExecuteHandlerOptions {
|
|
56
|
+
routeInfos: RouteInfos;
|
|
57
|
+
method: string;
|
|
58
|
+
request: Request;
|
|
59
|
+
fullUrl: string;
|
|
60
|
+
locals: unknown;
|
|
61
|
+
responseInit: ResponseInit;
|
|
62
|
+
premises: PremisesDescriptor | null;
|
|
63
|
+
routeTemplateOptions: Parameters<typeof renderRouteTemplate>[0];
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Result from `executeHandler`:
|
|
67
|
+
*
|
|
68
|
+
* - `{ type: 'response', value }` — return this response directly
|
|
69
|
+
* - `{ type: 'output', value }` — pass to response building
|
|
70
|
+
* - `{ type: 'fallthrough' }` — no handler, proceed to template-only render
|
|
71
|
+
*/
|
|
72
|
+
export type ExecuteHandlerResult = {
|
|
73
|
+
type: 'response';
|
|
74
|
+
value: StandardResponse;
|
|
75
|
+
} | {
|
|
76
|
+
type: 'output';
|
|
77
|
+
value: Readable | Response | null;
|
|
78
|
+
} | {
|
|
79
|
+
type: 'fallthrough';
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Dispatch the user's route handler (top-level function or method-map).
|
|
83
|
+
*
|
|
84
|
+
* This determines whether to call the handler, which method to use,
|
|
85
|
+
* and whether to short-circuit with premises or a 405.
|
|
86
|
+
*/
|
|
87
|
+
export declare function executeHandler(options: ExecuteHandlerOptions): Promise<ExecuteHandlerResult>;
|
|
88
|
+
/**
|
|
89
|
+
* Render a page that has no handler — just a document/template.
|
|
90
|
+
* Handles premises short-circuits.
|
|
91
|
+
*/
|
|
92
|
+
export declare function renderWithoutHandler(options: {
|
|
93
|
+
premises: PremisesDescriptor | null;
|
|
94
|
+
routeTemplateOptions: Parameters<typeof renderRouteTemplate>[0];
|
|
95
|
+
}): Promise<StandardResponse | Readable | null>;
|
|
96
|
+
export declare function isRedirect(response: Response): {
|
|
97
|
+
location: string;
|
|
98
|
+
} | null;
|
|
99
|
+
/**
|
|
100
|
+
* Convert a handler/render output (Response or Readable stream) into
|
|
101
|
+
* the final `HandlerResult` shape expected by adapters.
|
|
102
|
+
*/
|
|
103
|
+
export declare function buildResponse(options: {
|
|
104
|
+
output: Readable | Response | null;
|
|
105
|
+
responseInit: ResponseInit;
|
|
106
|
+
vite: ViteDevServer | undefined;
|
|
107
|
+
logger: Logger;
|
|
108
|
+
}): HandlerResult;
|
|
109
|
+
//# sourceMappingURL=request-pipeline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-pipeline.d.ts","sourceRoot":"","sources":["../../src/server/request-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAIvC,OAAO,KAAK,EAAc,MAAM,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAE9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAIvD,MAAM,MAAM,gBAAgB,GAAG;IAC9B,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,CAAC,EAAE,KAAK,CAAC;IACb,IAAI,CAAC,EAAE,KAAK,CAAC;CACb,CAAC;AACF,MAAM,MAAM,wBAAwB,GAAG;IACtC,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,YAAY,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,gBAAgB,GAAG,wBAAwB,GAAG,IAAI,CAAC;AAE/E,eAAO,MAAM,iBAAiB;;CAA2C,CAAC;AAE1E,eAAO,MAAM,eAAe;;;CAGlB,CAAC;AAEX,+DAA+D;AAC/D,MAAM,WAAW,kBAAkB;IAClC,cAAc,EAAE,OAAO,CAAC;IACxB,YAAY,EAAE,OAAO,CAAC;CACtB;AAID;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEvD;AAID;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC9B,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,aAAa,GAC1B,kBAAkB,GAAG,IAAI,CAY3B;AAaD,MAAM,WAAW,qBAAqB;IACrC,UAAU,EAAE,UAAU,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,EAAE,YAAY,CAAC;IAC3B,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACpC,oBAAoB,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC;CAChE;AAED;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,GAC7B;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,gBAAgB,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,QAAQ,GAAG,QAAQ,GAAG,IAAI,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC;AAE3B;;;;;GAKG;AACH,wBAAsB,cAAc,CACnC,OAAO,EAAE,qBAAqB,GAC5B,OAAO,CAAC,oBAAoB,CAAC,CAyF/B;AAID;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE;IACnD,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACpC,oBAAoB,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC;CAChE,GAAG,OAAO,CAAC,gBAAgB,GAAG,QAAQ,GAAG,IAAI,CAAC,CAoB9C;AAID,wBAAgB,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAM1E;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE;IACtC,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,IAAI,CAAC;IACnC,YAAY,EAAE,YAAY,CAAC;IAC3B,IAAI,EAAE,aAAa,GAAG,SAAS,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;CACf,GAAG,aAAa,CAqDhB"}
|