@gracile/engine 0.8.2 → 0.9.0-next.10
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/dev/ssr-ce-tracker.d.ts +27 -0
- package/dist/dev/ssr-ce-tracker.d.ts.map +1 -0
- package/dist/dev/ssr-ce-tracker.js +113 -0
- package/dist/errors/create-vite-better-error.js +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +56 -256
- package/dist/render/light-dom.d.ts +2 -0
- package/dist/render/light-dom.d.ts.map +1 -0
- package/dist/render/light-dom.js +31 -0
- package/dist/render/{utils.d.ts → lit-ssr.d.ts} +1 -1
- package/dist/render/lit-ssr.d.ts.map +1 -0
- 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 +4 -3
- package/dist/render/route-template.d.ts.map +1 -1
- package/dist/render/route-template.js +41 -87
- package/dist/routes/collect.d.ts +5 -1
- package/dist/routes/collect.d.ts.map +1 -1
- package/dist/routes/collect.js +11 -6
- package/dist/routes/load-module.d.ts.map +1 -1
- package/dist/routes/load-module.js +5 -2
- 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 +12 -3
- package/dist/server/adapters/hono.d.ts +1 -1
- package/dist/server/adapters/hono.d.ts.map +1 -1
- package/dist/server/adapters/hono.js +1 -1
- package/dist/server/adapters/node.d.ts +1 -1
- package/dist/server/adapters/node.d.ts.map +1 -1
- package/dist/server/adapters/node.js +2 -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 +78 -173
- package/dist/server/{utils.d.ts → utilities.d.ts} +1 -1
- package/dist/server/utilities.d.ts.map +1 -0
- 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 +48 -0
- package/dist/user-config.d.ts.map +1 -1
- package/dist/vite/build-routes.d.ts +1 -2
- package/dist/vite/build-routes.d.ts.map +1 -1
- package/dist/vite/build-routes.js +0 -98
- package/dist/vite/plugin-build-environment.d.ts +21 -0
- package/dist/vite/plugin-build-environment.d.ts.map +1 -0
- package/dist/vite/plugin-build-environment.js +83 -0
- package/dist/vite/plugin-ce-tracker.d.ts +19 -0
- package/dist/vite/plugin-ce-tracker.d.ts.map +1 -0
- package/dist/vite/plugin-ce-tracker.js +87 -0
- package/dist/vite/plugin-client-build.d.ts +20 -0
- package/dist/vite/plugin-client-build.d.ts.map +1 -0
- package/dist/vite/plugin-client-build.js +55 -0
- package/dist/vite/plugin-html-routes-build.d.ts +22 -0
- package/dist/vite/plugin-html-routes-build.d.ts.map +1 -0
- package/dist/vite/plugin-html-routes-build.js +140 -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 +61 -0
- package/dist/vite/plugin-server-build.d.ts +43 -0
- package/dist/vite/plugin-server-build.d.ts.map +1 -0
- package/dist/vite/plugin-server-build.js +108 -0
- package/dist/vite/plugin-shared-state.d.ts +33 -0
- package/dist/vite/plugin-shared-state.d.ts.map +1 -0
- package/dist/vite/plugin-shared-state.js +23 -0
- package/dist/vite/virtual-routes.d.ts +8 -5
- package/dist/vite/virtual-routes.d.ts.map +1 -1
- package/dist/vite/virtual-routes.js +32 -31
- package/package.json +11 -11
- package/dist/render/utils.d.ts.map +0 -1
- package/dist/server/utils.d.ts.map +0 -1
- /package/dist/render/{utils.js → lit-ssr.js} +0 -0
- /package/dist/server/{utils.js → utilities.js} +0 -0
package/dist/server/request.js
CHANGED
|
@@ -1,59 +1,56 @@
|
|
|
1
1
|
import { Readable } from 'node:stream';
|
|
2
|
-
import * as assert from '@gracile/internal-utils/assertions';
|
|
3
2
|
import { getLogger } from '@gracile/internal-utils/logger/helpers';
|
|
4
3
|
import c from 'picocolors';
|
|
5
|
-
// import { GracileError } from '../errors/errors.js';
|
|
6
4
|
import { builtIn404Page, builtInErrorPage } from '../errors/pages.js';
|
|
7
5
|
import { renderRouteTemplate } from '../render/route-template.js';
|
|
8
|
-
import { renderLitTemplate } from '../render/
|
|
6
|
+
import { renderLitTemplate } from '../render/lit-ssr.js';
|
|
9
7
|
import { getRoute } from '../routes/match.js';
|
|
10
|
-
|
|
11
|
-
const PREMISE_REGEXES = {
|
|
12
|
-
properties: /\/__(.*?)\.props\.json$/,
|
|
13
|
-
document: /\/__(.*?)\.doc\.html$/,
|
|
14
|
-
};
|
|
15
|
-
export function isRedirect(response) {
|
|
16
|
-
const location = response.headers.get('location');
|
|
17
|
-
if (response.status >= 300 && response.status <= 303 && location) {
|
|
18
|
-
return { location };
|
|
19
|
-
}
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
8
|
+
import { CONTENT_TYPE_HTML, rewriteHiddenRoutes, resolvePremises, executeHandler, renderWithoutHandler, buildResponse, } from './request-pipeline.js';
|
|
22
9
|
export function createGracileHandler({ vite, routes, routeImports, routeAssets, root, serverMode, gracileConfig, }) {
|
|
23
10
|
const logger = getLogger();
|
|
24
11
|
const middleware = async (request, locals) => {
|
|
25
12
|
const { url: requestedUrl, method } = request;
|
|
26
13
|
let emitViteBetterError = null;
|
|
27
14
|
if (vite)
|
|
28
|
-
emitViteBetterError =
|
|
15
|
+
emitViteBetterError =
|
|
16
|
+
await import('../errors/create-vite-better-error.js').then(({ emitViteBetterError: error }) => error);
|
|
29
17
|
try {
|
|
30
|
-
// MARK: Rewrite hidden route siblings
|
|
31
|
-
const fullUrl = requestedUrl
|
|
32
|
-
// MARK: Setup premises
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
const allowPremises = Boolean(gracileConfig.pages?.premises?.expose);
|
|
36
|
-
if (allowPremises === false && (propertiesOnly || documentOnly))
|
|
37
|
-
throw new Error('Accessed a page premise but they are not activated. You must enable `pages.premises.expose`.');
|
|
38
|
-
const premises = allowPremises ? { propertiesOnly, documentOnly } : null;
|
|
39
|
-
// MARK: Get route infos
|
|
18
|
+
// MARK: 1. Rewrite hidden route siblings
|
|
19
|
+
const fullUrl = rewriteHiddenRoutes(requestedUrl);
|
|
20
|
+
// MARK: 2. Setup premises
|
|
21
|
+
const premises = resolvePremises(requestedUrl, gracileConfig);
|
|
22
|
+
// MARK: 3. Route resolution + 404 fallback
|
|
40
23
|
const routeOptions = {
|
|
41
24
|
url: fullUrl,
|
|
42
25
|
vite,
|
|
43
26
|
routes,
|
|
44
27
|
routeImports,
|
|
28
|
+
trailingSlash: gracileConfig.trailingSlash ?? 'ignore',
|
|
45
29
|
};
|
|
46
30
|
const responseInit = {};
|
|
47
|
-
|
|
48
|
-
//
|
|
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;
|
|
49
39
|
if (routeInfos === null) {
|
|
50
40
|
responseInit.status = 404;
|
|
41
|
+
// Use 'ignore' for the internal 404 lookup to avoid redirect loops.
|
|
51
42
|
const url = new URL('/404/', fullUrl).href;
|
|
52
|
-
const options = {
|
|
53
|
-
|
|
54
|
-
|
|
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;
|
|
55
53
|
}
|
|
56
|
-
// MARK: fallback 404
|
|
57
54
|
if (routeInfos === null) {
|
|
58
55
|
const page = builtIn404Page(new URL(fullUrl).pathname, Boolean(vite));
|
|
59
56
|
return {
|
|
@@ -65,163 +62,49 @@ export function createGracileHandler({ vite, routes, routeImports, routeAssets,
|
|
|
65
62
|
const routeTemplateOptions = {
|
|
66
63
|
url: fullUrl,
|
|
67
64
|
vite,
|
|
68
|
-
mode: 'dev',
|
|
65
|
+
mode: 'dev',
|
|
69
66
|
routeAssets,
|
|
70
67
|
root,
|
|
71
68
|
serverMode,
|
|
72
69
|
routeInfos,
|
|
73
70
|
docOnly: premises?.documentOnly,
|
|
71
|
+
renderInfo: gracileConfig.litSsr?.renderInfo,
|
|
74
72
|
};
|
|
75
73
|
logger.info(`[${c.yellow(method)}] ${c.yellow(fullUrl)}`, {
|
|
76
74
|
timestamp: true,
|
|
77
75
|
});
|
|
76
|
+
// MARK: 4. Handler dispatch
|
|
78
77
|
let output;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
});
|
|
94
|
-
// MARK: Run user middleware
|
|
95
|
-
// NOTE: Experimental
|
|
96
|
-
/// eslint-disable-next-line no-inner-declarations
|
|
97
|
-
// async function useHandler() {}
|
|
98
|
-
// if (vite) {
|
|
99
|
-
// const middleware = await vite
|
|
100
|
-
// .ssrLoadModule('/src/middleware.ts')
|
|
101
|
-
// .catch(() => null)
|
|
102
|
-
// .then((m) => m.default);
|
|
103
|
-
// if (middleware)
|
|
104
|
-
// await middleware(
|
|
105
|
-
// routeContext,
|
|
106
|
-
// async () => {
|
|
107
|
-
// await useHandler();
|
|
108
|
-
// },
|
|
109
|
-
// );
|
|
110
|
-
// else await useHandler();
|
|
111
|
-
// } else {
|
|
112
|
-
// await useHandler();
|
|
113
|
-
// }
|
|
114
|
-
//
|
|
115
|
-
// MARK: Handler(s)
|
|
116
|
-
const hasTopLevelHandler = typeof handler === 'function';
|
|
117
|
-
if (hasTopLevelHandler || method in handler) {
|
|
118
|
-
const handlerWithMethod = hasTopLevelHandler
|
|
119
|
-
? handler
|
|
120
|
-
: handler[method];
|
|
121
|
-
if (typeof handlerWithMethod !== 'function')
|
|
122
|
-
throw new TypeError('Handler must be a function.');
|
|
123
|
-
const handlerOutput = await Promise.resolve(handlerWithMethod(routeContext));
|
|
124
|
-
if (assert.isResponseOrPatchedResponse(handlerOutput))
|
|
125
|
-
output = handlerOutput;
|
|
126
|
-
else {
|
|
127
|
-
routeTemplateOptions.routeInfos.props = hasTopLevelHandler
|
|
128
|
-
? handlerOutput
|
|
129
|
-
: { [method]: handlerOutput };
|
|
130
|
-
if (premises?.documentOnly) {
|
|
131
|
-
const { document } = await renderRouteTemplate(routeTemplateOptions);
|
|
132
|
-
return {
|
|
133
|
-
response: new Response(document, {
|
|
134
|
-
headers: { ...CONTENT_TYPE_HTML },
|
|
135
|
-
}),
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
if (premises?.propertiesOnly)
|
|
139
|
-
return {
|
|
140
|
-
response: Response.json(routeTemplateOptions.routeInfos.props),
|
|
141
|
-
};
|
|
142
|
-
output = await renderRouteTemplate(routeTemplateOptions).then((r) => r.output);
|
|
143
|
-
}
|
|
144
|
-
// MARK: No GET, render page
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
const statusText = `This route doesn't handle the \`${method}\` method!`;
|
|
148
|
-
return {
|
|
149
|
-
response: new Response(statusText, { status: 405, statusText }),
|
|
150
|
-
};
|
|
151
|
-
}
|
|
78
|
+
const handlerResult = await executeHandler({
|
|
79
|
+
routeInfos,
|
|
80
|
+
method,
|
|
81
|
+
request,
|
|
82
|
+
fullUrl,
|
|
83
|
+
locals,
|
|
84
|
+
responseInit,
|
|
85
|
+
premises,
|
|
86
|
+
routeTemplateOptions,
|
|
87
|
+
});
|
|
88
|
+
if (handlerResult.type === 'response')
|
|
89
|
+
return handlerResult.value;
|
|
90
|
+
if (handlerResult.type === 'output') {
|
|
91
|
+
output = handlerResult.value;
|
|
152
92
|
}
|
|
153
93
|
else {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (premises?.propertiesOnly)
|
|
163
|
-
return {
|
|
164
|
-
response: Response.json(routeTemplateOptions.routeInfos.props || {}),
|
|
165
|
-
};
|
|
166
|
-
output = await renderRouteTemplate(routeTemplateOptions).then((r) => r.output);
|
|
167
|
-
}
|
|
168
|
-
// MARK: Return response
|
|
169
|
-
// NOTE: try directly with the requestPonyfill. This might not be necessary
|
|
170
|
-
if (assert.isResponseOrPatchedResponse(output)) {
|
|
171
|
-
const redirect = isRedirect(output);
|
|
172
|
-
if (redirect?.location)
|
|
173
|
-
return {
|
|
174
|
-
response: Response.redirect(redirect.location, output.status),
|
|
175
|
-
};
|
|
176
|
-
return { response: output };
|
|
177
|
-
// MARK: Stream page render
|
|
178
|
-
}
|
|
179
|
-
// MARK: Page stream error
|
|
180
|
-
if (output instanceof Readable) {
|
|
181
|
-
responseInit.headers = {
|
|
182
|
-
...responseInit.headers,
|
|
183
|
-
...CONTENT_TYPE_HTML,
|
|
184
|
-
};
|
|
185
|
-
return {
|
|
186
|
-
body: output.on('error', (error) => {
|
|
187
|
-
const errorMessage = `[SSR Error] There was an error while rendering a template chunk on server-side.\n` +
|
|
188
|
-
`It was omitted from the resulting HTML.\n`;
|
|
189
|
-
if (vite) {
|
|
190
|
-
logger.error(errorMessage + error.stack);
|
|
191
|
-
// emitViteBetterError(new GracileError(GracileErrorData.FailedToGlobalLogger), vite);
|
|
192
|
-
const payload = {
|
|
193
|
-
type: 'error',
|
|
194
|
-
// FIXME: Use the emitViteBetterError instead (but flaky for now with streaming)
|
|
195
|
-
// err: new GracileError({}),
|
|
196
|
-
err: {
|
|
197
|
-
name: 'StreamingError',
|
|
198
|
-
message: errorMessage,
|
|
199
|
-
stack: error.stack,
|
|
200
|
-
hint: 'This is often caused by a wrong template location dynamic interpolation.',
|
|
201
|
-
cause: error,
|
|
202
|
-
// highlightedCode: error.message,
|
|
203
|
-
},
|
|
204
|
-
};
|
|
205
|
-
//
|
|
206
|
-
setTimeout(() => {
|
|
207
|
-
// @ts-expect-error ...........
|
|
208
|
-
vite.hot.send(payload);
|
|
209
|
-
// NOTE: Arbitrary value. Lower seems to be too fast, higher is not guaranteed to work.
|
|
210
|
-
}, 200);
|
|
211
|
-
}
|
|
212
|
-
else {
|
|
213
|
-
logger.error(errorMessage);
|
|
214
|
-
}
|
|
215
|
-
}),
|
|
216
|
-
init: responseInit,
|
|
217
|
-
};
|
|
94
|
+
// MARK: 5. Template-only render (no handler)
|
|
95
|
+
const renderResult = await renderWithoutHandler({
|
|
96
|
+
premises,
|
|
97
|
+
routeTemplateOptions,
|
|
98
|
+
});
|
|
99
|
+
if (renderResult && 'response' in renderResult)
|
|
100
|
+
return renderResult;
|
|
101
|
+
output = renderResult;
|
|
218
102
|
}
|
|
219
|
-
|
|
103
|
+
// MARK: 6. Build final response
|
|
104
|
+
return buildResponse({ output, responseInit, vite, logger });
|
|
220
105
|
// MARK: Errors
|
|
221
106
|
}
|
|
222
107
|
catch (error) {
|
|
223
|
-
// const safeError = createSafeError(error);
|
|
224
|
-
// TODO: User defined dev/runtime 500 error
|
|
225
108
|
const ultimateErrorPage = vite && emitViteBetterError
|
|
226
109
|
? await emitViteBetterError({ vite, error: error })
|
|
227
110
|
: await renderLitTemplate(builtInErrorPage(error.name));
|
|
@@ -236,3 +119,25 @@ export function createGracileHandler({ vite, routes, routeImports, routeAssets,
|
|
|
236
119
|
};
|
|
237
120
|
return middleware;
|
|
238
121
|
}
|
|
122
|
+
// MARK: Run user middleware
|
|
123
|
+
// NOTE: Experimental
|
|
124
|
+
/// eslint-disable-next-line no-inner-declarations
|
|
125
|
+
// async function useHandler() {}
|
|
126
|
+
// if (vite) {
|
|
127
|
+
// const middleware = await vite
|
|
128
|
+
// .ssrLoadModule('/src/middleware.ts')
|
|
129
|
+
// .catch(() => null)
|
|
130
|
+
// .then((m) => m.default);
|
|
131
|
+
// if (middleware)
|
|
132
|
+
// await middleware(
|
|
133
|
+
// routeContext,
|
|
134
|
+
// async () => {
|
|
135
|
+
// await useHandler();
|
|
136
|
+
// },
|
|
137
|
+
// );
|
|
138
|
+
// else await useHandler();
|
|
139
|
+
// } else {
|
|
140
|
+
// await useHandler();
|
|
141
|
+
// }
|
|
142
|
+
//
|
|
143
|
+
export { isRedirect, } from './request-pipeline.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../../src/server/utilities.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAY5C;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,QA0C5D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/test/init.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Side-effect import: initializes the Gracile logger globally.
|
|
3
|
+
* Import this at the top of any engine unit test that touches modules
|
|
4
|
+
* which call `getLogger()` at module scope.
|
|
5
|
+
*/
|
|
6
|
+
import { createLogger } from '@gracile/internal-utils/logger/helpers';
|
|
7
|
+
createLogger();
|
package/dist/user-config.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { RenderInfo } from '@lit-labs/ssr';
|
|
1
2
|
import type { Connect } from 'vite';
|
|
2
3
|
/**
|
|
3
4
|
* @example
|
|
@@ -37,6 +38,19 @@ export interface GracileConfig {
|
|
|
37
38
|
* @defaultValue 'static'
|
|
38
39
|
*/
|
|
39
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';
|
|
40
54
|
/**
|
|
41
55
|
* Settings for the development mode.
|
|
42
56
|
*/
|
|
@@ -93,6 +107,40 @@ export interface GracileConfig {
|
|
|
93
107
|
exclude?: string[];
|
|
94
108
|
};
|
|
95
109
|
};
|
|
110
|
+
litSsr?: {
|
|
111
|
+
/**
|
|
112
|
+
* Lets you extend Gracile's SSR pipeline with
|
|
113
|
+
* custom Lit SSR `ElementRenderer` subclasses. This is the foundation for
|
|
114
|
+
* features like [Islands](/docs/add-ons/islands/), which register a renderer
|
|
115
|
+
* for the `<is-land>` custom element to server-render components from other
|
|
116
|
+
* UI frameworks.
|
|
117
|
+
*
|
|
118
|
+
* In most cases, you do **not** set this option manually — add-on plugins
|
|
119
|
+
* (like `gracileIslands()`) register their renderers automatically via the
|
|
120
|
+
* plugin context communication channel. However,
|
|
121
|
+
* you can use it directly for advanced use cases:
|
|
122
|
+
*
|
|
123
|
+
* ```ts
|
|
124
|
+
* import { gracile } from '@gracile/gracile/plugin';
|
|
125
|
+
* import { defineConfig } from 'vite';
|
|
126
|
+
*
|
|
127
|
+
* export default defineConfig({
|
|
128
|
+
* plugins: [
|
|
129
|
+
* gracile({
|
|
130
|
+
* litSsr: {
|
|
131
|
+
* renderInfo: {
|
|
132
|
+
* elementRenderers: [
|
|
133
|
+
* // Your custom ElementRenderer subclass
|
|
134
|
+
* ],
|
|
135
|
+
* },
|
|
136
|
+
* },
|
|
137
|
+
* }),
|
|
138
|
+
* ],
|
|
139
|
+
* });
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
renderInfo?: Partial<RenderInfo>;
|
|
143
|
+
};
|
|
96
144
|
/**
|
|
97
145
|
* Future, unstable features flags.
|
|
98
146
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-config.d.ts","sourceRoot":"","sources":["../src/user-config.ts"],"names":[],"mappings":"AAAA,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;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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type ViteDevServer } from 'vite';
|
|
2
2
|
import { type RenderedRouteDefinition } from '../routes/render.js';
|
|
3
3
|
import type { RoutesManifest } from '../routes/route.js';
|
|
4
4
|
import type { GracileConfig } from '../user-config.js';
|
|
@@ -12,6 +12,5 @@ export declare const buildRoutes: ({ routes, viteServerForBuild, root, gracileCo
|
|
|
12
12
|
routes: RoutesManifest;
|
|
13
13
|
renderedRoutes: RenderedRouteDefinition[];
|
|
14
14
|
inputList: string[];
|
|
15
|
-
plugin: Plugin[];
|
|
16
15
|
}>;
|
|
17
16
|
//# sourceMappingURL=build-routes.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-routes.d.ts","sourceRoot":"","sources":["../../src/vite/build-routes.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"build-routes.d.ts","sourceRoot":"","sources":["../../src/vite/build-routes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,MAAM,CAAC;AAExD,OAAO,EAEN,KAAK,uBAAuB,EAC5B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AASvD,eAAO,MAAM,WAAW,GAAU,kEAM/B;IACF,MAAM,EAAE,cAAc,CAAC;IACvB,kBAAkB,EAAE,aAAa,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,aAAa,CAAC;IAC7B,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB,KAAG,OAAO,CAAC;IACX,MAAM,EAAE,cAAc,CAAC;IACvB,cAAc,EAAE,uBAAuB,EAAE,CAAC;IAC1C,SAAS,EAAE,MAAM,EAAE,CAAC;CACpB,CAkCA,CAAC"}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { basename, extname, join } from 'node:path';
|
|
2
1
|
import { createFilter } from 'vite';
|
|
3
2
|
import { renderRoutes, } from '../routes/render.js';
|
|
4
|
-
import { REGEX_TAG_LINK, REGEX_TAG_SCRIPT } from '../render/route-template.js';
|
|
5
3
|
function stripPremises(input) {
|
|
6
4
|
return input
|
|
7
5
|
.replace(/index\.html$/, '__index.doc.html')
|
|
@@ -9,7 +7,6 @@ function stripPremises(input) {
|
|
|
9
7
|
.replace(/500\.html$/, '__500.doc.html');
|
|
10
8
|
}
|
|
11
9
|
export const buildRoutes = async ({ routes, viteServerForBuild, root, gracileConfig, serverMode = false, }) => {
|
|
12
|
-
// TODO: extract upstream, return just the plugins
|
|
13
10
|
const { renderedRoutes } = await renderRoutes({
|
|
14
11
|
vite: viteServerForBuild,
|
|
15
12
|
serverMode,
|
|
@@ -35,100 +32,5 @@ export const buildRoutes = async ({ routes, viteServerForBuild, root, gracileCon
|
|
|
35
32
|
routes,
|
|
36
33
|
renderedRoutes,
|
|
37
34
|
inputList,
|
|
38
|
-
plugin: [
|
|
39
|
-
{
|
|
40
|
-
name: 'gracile-html-routes',
|
|
41
|
-
apply: 'build',
|
|
42
|
-
enforce: 'pre',
|
|
43
|
-
resolveId(id) {
|
|
44
|
-
if (extname(id) === '.html' && inputList.includes(id))
|
|
45
|
-
return join(root, id);
|
|
46
|
-
return null;
|
|
47
|
-
},
|
|
48
|
-
load(id) {
|
|
49
|
-
if (extname(id) === '.html') {
|
|
50
|
-
if (['index.html', '404.html', '500.html'].includes(basename(id))) {
|
|
51
|
-
const content = renderedRoutes.find((index) => index.absoluteId === id)?.html;
|
|
52
|
-
if (content)
|
|
53
|
-
return content;
|
|
54
|
-
}
|
|
55
|
-
if (gracileConfig.pages?.premises?.expose) {
|
|
56
|
-
if (basename(id).endsWith('doc.html')) {
|
|
57
|
-
const content = renderedRoutes.find((index) => stripPremises(index.absoluteId) === id)?.static.document;
|
|
58
|
-
if (content)
|
|
59
|
-
return content;
|
|
60
|
-
}
|
|
61
|
-
const content = renderedRoutes.find((index) => index.name === basename(id))?.html;
|
|
62
|
-
if (content)
|
|
63
|
-
return content;
|
|
64
|
-
// return '';}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return null;
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
name: 'gracile-collect-handler-assets',
|
|
72
|
-
enforce: 'post',
|
|
73
|
-
buildStart() {
|
|
74
|
-
if (!gracileConfig.pages?.premises?.expose || !premisesFilter)
|
|
75
|
-
return;
|
|
76
|
-
for (const route of renderedRoutes) {
|
|
77
|
-
if (premisesFilter(route.name) === false)
|
|
78
|
-
continue;
|
|
79
|
-
if (serverMode && route.savePrerender !== true)
|
|
80
|
-
continue;
|
|
81
|
-
const fileNameParts = route.name.split('/');
|
|
82
|
-
const last = fileNameParts.pop();
|
|
83
|
-
const properties = last?.replace(/(.*)\.html$/, (_, r) => `__${r}.props.json`);
|
|
84
|
-
if (!properties)
|
|
85
|
-
throw new Error('No props.');
|
|
86
|
-
fileNameParts.push(properties);
|
|
87
|
-
const fileName = fileNameParts.join('/');
|
|
88
|
-
this.emitFile({
|
|
89
|
-
fileName,
|
|
90
|
-
type: 'asset',
|
|
91
|
-
source: JSON.stringify(route.static.props ?? {}),
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
generateBundle(_, bundle) {
|
|
96
|
-
if (serverMode === false)
|
|
97
|
-
return;
|
|
98
|
-
for (const fileKey in bundle) {
|
|
99
|
-
const file = bundle[fileKey];
|
|
100
|
-
if (fileKey.endsWith('.html') && file && 'source' in file) {
|
|
101
|
-
const source = file.source.toString();
|
|
102
|
-
const collectedAssets = [
|
|
103
|
-
...[...source.matchAll(REGEX_TAG_SCRIPT)]
|
|
104
|
-
.map((v) => v[0])
|
|
105
|
-
// NOTE: Too brittle
|
|
106
|
-
.filter((v) => v.includes(`type="module"`)),
|
|
107
|
-
...[...source.matchAll(REGEX_TAG_LINK)]
|
|
108
|
-
.map((v) => v[0])
|
|
109
|
-
// NOTE: Too brittle
|
|
110
|
-
.filter((v) => /rel="(stylesheet|modulepreload)"/.test(v)),
|
|
111
|
-
].join('\n');
|
|
112
|
-
// NOTE: Not used (for now?)
|
|
113
|
-
// file.source = alteredContent;
|
|
114
|
-
const route = renderedRoutes.find((r) => {
|
|
115
|
-
if (gracileConfig.pages?.premises?.expose)
|
|
116
|
-
return (`/${r.name}` ===
|
|
117
|
-
`/${fileKey}`.replace(/(.*?)\/__(.*?)\.doc\.html$/, (a, b, c) => `${b}/${c}.html`));
|
|
118
|
-
return r.name === fileKey;
|
|
119
|
-
});
|
|
120
|
-
if (route)
|
|
121
|
-
route.handlerAssets = collectedAssets;
|
|
122
|
-
if (route?.savePrerender !== true) {
|
|
123
|
-
delete bundle[fileKey];
|
|
124
|
-
// NOTE: Not sure if it's useful
|
|
125
|
-
if (route?.html)
|
|
126
|
-
route.html = null;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
},
|
|
131
|
-
},
|
|
132
|
-
],
|
|
133
35
|
};
|
|
134
36
|
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite plugin: build environment configuration.
|
|
3
|
+
*
|
|
4
|
+
* Owns all Environment API concerns for builds:
|
|
5
|
+
* - Top-level config: `builder.sharedConfigBuild`, `ssr.external` (server mode)
|
|
6
|
+
* - `configEnvironment('client')`: rollupOptions.input, outDir
|
|
7
|
+
* - `configEnvironment('ssr')`: outDir, SSR-specific build options, rollupOptions
|
|
8
|
+
* - `buildApp()`: orchestrates build order (client-only or client → SSR)
|
|
9
|
+
*
|
|
10
|
+
* `rollupOptions.input` must be set via `configEnvironment` (not the
|
|
11
|
+
* top-level `config` hook) because `builder.build(env)` uses
|
|
12
|
+
* environment-resolved config, not the top-level config.
|
|
13
|
+
*
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
import type { PluginOption } from 'vite';
|
|
17
|
+
import type { PluginSharedState } from './plugin-shared-state.js';
|
|
18
|
+
export declare function gracileBuildEnvironmentPlugin({ state, }: {
|
|
19
|
+
state: PluginSharedState;
|
|
20
|
+
}): PluginOption;
|
|
21
|
+
//# sourceMappingURL=plugin-build-environment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-build-environment.d.ts","sourceRoot":"","sources":["../../src/vite/plugin-build-environment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,MAAM,CAAC;AAEtD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,wBAAgB,6BAA6B,CAAC,EAC7C,KAAK,GACL,EAAE;IACF,KAAK,EAAE,iBAAiB,CAAC;CACzB,GAAG,YAAY,CA4Ef"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite plugin: build environment configuration.
|
|
3
|
+
*
|
|
4
|
+
* Owns all Environment API concerns for builds:
|
|
5
|
+
* - Top-level config: `builder.sharedConfigBuild`, `ssr.external` (server mode)
|
|
6
|
+
* - `configEnvironment('client')`: rollupOptions.input, outDir
|
|
7
|
+
* - `configEnvironment('ssr')`: outDir, SSR-specific build options, rollupOptions
|
|
8
|
+
* - `buildApp()`: orchestrates build order (client-only or client → SSR)
|
|
9
|
+
*
|
|
10
|
+
* `rollupOptions.input` must be set via `configEnvironment` (not the
|
|
11
|
+
* top-level `config` hook) because `builder.build(env)` uses
|
|
12
|
+
* environment-resolved config, not the top-level config.
|
|
13
|
+
*
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
export function gracileBuildEnvironmentPlugin({ state, }) {
|
|
18
|
+
const isServerMode = state.outputMode === 'server';
|
|
19
|
+
return {
|
|
20
|
+
name: 'vite-plugin-gracile-build-env',
|
|
21
|
+
apply: 'build',
|
|
22
|
+
config() {
|
|
23
|
+
return {
|
|
24
|
+
...(isServerMode ? { ssr: { external: ['@gracile/gracile'] } } : {}),
|
|
25
|
+
builder: { sharedConfigBuild: true },
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
configEnvironment(name, config) {
|
|
29
|
+
if (name === 'client') {
|
|
30
|
+
if (!state.clientBuildInputList)
|
|
31
|
+
return null;
|
|
32
|
+
return {
|
|
33
|
+
build: {
|
|
34
|
+
rollupOptions: {
|
|
35
|
+
input: state.clientBuildInputList,
|
|
36
|
+
},
|
|
37
|
+
outDir: join(config.build?.outDir || 'dist', isServerMode ? 'client' : ''),
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
if (name === 'ssr' && isServerMode) {
|
|
42
|
+
return {
|
|
43
|
+
build: {
|
|
44
|
+
outDir: 'dist/server',
|
|
45
|
+
copyPublicDir: false,
|
|
46
|
+
ssrEmitAssets: true,
|
|
47
|
+
cssMinify: true,
|
|
48
|
+
cssCodeSplit: true,
|
|
49
|
+
rollupOptions: {
|
|
50
|
+
input: 'entrypoint.js',
|
|
51
|
+
output: {
|
|
52
|
+
entryFileNames: '[name].js',
|
|
53
|
+
assetFileNames: (chunkInfo) => {
|
|
54
|
+
if (chunkInfo.name) {
|
|
55
|
+
const fileName = state.clientAssets[chunkInfo.name];
|
|
56
|
+
if (fileName)
|
|
57
|
+
return fileName;
|
|
58
|
+
return `assets/${chunkInfo.name.replace(/\.(.*)$/, '')}-[hash].[ext]`;
|
|
59
|
+
}
|
|
60
|
+
return 'assets/[name]-[hash].[ext]';
|
|
61
|
+
},
|
|
62
|
+
chunkFileNames: 'chunk/[name].js',
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
},
|
|
70
|
+
async buildApp(builder) {
|
|
71
|
+
const client = builder.environments['client'];
|
|
72
|
+
if (!client)
|
|
73
|
+
throw new Error('Missing client build environment.');
|
|
74
|
+
await builder.build(client);
|
|
75
|
+
if (isServerMode) {
|
|
76
|
+
const ssr = builder.environments['ssr'];
|
|
77
|
+
if (!ssr)
|
|
78
|
+
throw new Error('Missing ssr build environment.');
|
|
79
|
+
await builder.build(ssr);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|