@gracile/engine 0.9.0-next.4 → 0.9.0-next.5
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/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 +12 -92
- package/dist/routes/collect.d.ts +4 -0
- package/dist/routes/collect.d.ts.map +1 -1
- package/dist/routes/collect.js +2 -1
- package/dist/routes/match.d.ts +26 -0
- package/dist/routes/match.d.ts.map +1 -1
- package/dist/routes/match.js +4 -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 +55 -169
- 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/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 +2 -2
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AActD;;;;;;;;;;;;;;;;GAgBG;AAIH,eAAO,MAAM,OAAO,GAAI,SAAS,aAAa,KAAG,GAAG,EAiEnD,CAAC;AAEF,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/plugin.js
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
|
-
import { join } from 'node:path';
|
|
2
|
-
import { rename, rm } from 'node:fs/promises';
|
|
3
1
|
import { createLogger } from '@gracile/internal-utils/logger/helpers';
|
|
4
2
|
import { getPluginContext, } from '@gracile/internal-utils/plugin-context';
|
|
5
|
-
import { getVersion } from '@gracile/internal-utils/version';
|
|
6
|
-
// import { betterErrors } from '@gracile-labs/better-errors/plugin';
|
|
7
|
-
import c from 'picocolors';
|
|
8
|
-
import { build, createServer } from 'vite';
|
|
9
|
-
import {} from './routes/render.js';
|
|
10
|
-
import { createDevelopmentHandler } from './dev/development.js';
|
|
11
|
-
import { nodeAdapter } from './server/adapters/node.js';
|
|
12
|
-
import { buildRoutes } from './vite/build-routes.js';
|
|
13
3
|
import { htmlRoutesLoader } from './vite/html-routes.js';
|
|
14
|
-
import { virtualRoutes, virtualRoutesClient } from './vite/virtual-routes.js';
|
|
15
4
|
import { hmrSsrReload } from './vite/hmr.js';
|
|
5
|
+
import { virtualRoutesClient } from './vite/virtual-routes.js';
|
|
6
|
+
import { createPluginSharedState } from './vite/plugin-shared-state.js';
|
|
7
|
+
import { gracileServePlugin } from './vite/plugin-serve.js';
|
|
8
|
+
import { gracileClientBuildPlugin } from './vite/plugin-client-build.js';
|
|
9
|
+
import { gracileCollectClientAssetsPlugin, gracileServerBuildPlugin, } from './vite/plugin-server-build.js';
|
|
16
10
|
let isClientBuilt = false;
|
|
17
11
|
/**
|
|
18
12
|
* The main Vite plugin for loading the Gracile framework.
|
|
@@ -36,274 +30,55 @@ let isClientBuilt = false;
|
|
|
36
30
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
37
31
|
export const gracile = (config) => {
|
|
38
32
|
const logger = createLogger();
|
|
39
|
-
const
|
|
40
|
-
const clientAssets = {};
|
|
41
|
-
const routes = new Map();
|
|
42
|
-
let renderedRoutes = null;
|
|
43
|
-
let root = null;
|
|
44
|
-
const gracileConfig = config || {};
|
|
33
|
+
const state = createPluginSharedState(config);
|
|
45
34
|
// HACK: Prevent duplicate client build for the SSR build step in server mode.
|
|
46
35
|
// TODO: Move to the new, clean, environments builders API.
|
|
47
36
|
if (isClientBuilt)
|
|
48
37
|
return [];
|
|
49
38
|
isClientBuilt = true;
|
|
50
39
|
const virtualRoutesForClient = virtualRoutesClient({
|
|
51
|
-
mode: outputMode,
|
|
52
|
-
routes,
|
|
53
|
-
|
|
54
|
-
// anymore.
|
|
55
|
-
gracileConfig,
|
|
56
|
-
// enabled: gracileConfig?.pages?.premises?.expose || false,
|
|
40
|
+
mode: state.outputMode,
|
|
41
|
+
routes: state.routes,
|
|
42
|
+
gracileConfig: state.gracileConfig,
|
|
57
43
|
});
|
|
58
44
|
let sharedPluginContext;
|
|
59
45
|
return [
|
|
46
|
+
// MARK: 1. Plugin context setup
|
|
60
47
|
{
|
|
61
48
|
name: 'vite-plugin-gracile-context',
|
|
62
|
-
config(
|
|
63
|
-
sharedPluginContext = getPluginContext(
|
|
64
|
-
gracileConfig.litSsr ??= { renderInfo: {} };
|
|
65
|
-
gracileConfig.litSsr.renderInfo =
|
|
49
|
+
config(viteConfig) {
|
|
50
|
+
sharedPluginContext = getPluginContext(viteConfig);
|
|
51
|
+
state.gracileConfig.litSsr ??= { renderInfo: {} };
|
|
52
|
+
state.gracileConfig.litSsr.renderInfo =
|
|
53
|
+
sharedPluginContext.litSsrRenderInfo;
|
|
66
54
|
},
|
|
67
55
|
},
|
|
68
|
-
//
|
|
69
|
-
// overlayImportPath: '@gracile/gracile/_internals/vite-custom-overlay',
|
|
70
|
-
// }),
|
|
71
|
-
// {
|
|
72
|
-
// name: 'gracile-routes-codegen',
|
|
73
|
-
// // watchChange(change) {
|
|
74
|
-
// // console.log({ change });
|
|
75
|
-
// // },
|
|
76
|
-
// resolveId(id) {
|
|
77
|
-
// const virtualModuleId = 'gracile:route';
|
|
78
|
-
// const resolvedVirtualModuleId = `\0${virtualModuleId}`;
|
|
79
|
-
// if (id === virtualModuleId) {
|
|
80
|
-
// return resolvedVirtualModuleId;
|
|
81
|
-
// }
|
|
82
|
-
// return null;
|
|
83
|
-
// },
|
|
84
|
-
// load(id) {
|
|
85
|
-
// const virtualModuleId = 'gracile:route';
|
|
86
|
-
// const resolvedVirtualModuleId = `\0${virtualModuleId}`;
|
|
87
|
-
// if (id === resolvedVirtualModuleId) {
|
|
88
|
-
// return `
|
|
89
|
-
// export function route(input){
|
|
90
|
-
// return input;
|
|
91
|
-
// }`;
|
|
92
|
-
// }
|
|
93
|
-
// return null;
|
|
94
|
-
// },
|
|
95
|
-
// },
|
|
56
|
+
// MARK: 2. HMR SSR reload
|
|
96
57
|
hmrSsrReload(),
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
config
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
return {
|
|
104
|
-
// NOTE: Supresses message: `Could not auto-determine entry point from rollupOptions or html files…`
|
|
105
|
-
// FIXME: It's not working when reloading the Vite config.
|
|
106
|
-
// Is user config, putting `optimizeDeps: { include: [] }` solve this.
|
|
107
|
-
optimizeDeps: { include: [] },
|
|
108
|
-
// NOTE: Useful? It breaks preview (expected)
|
|
109
|
-
appType: 'custom',
|
|
110
|
-
// resolve: {
|
|
111
|
-
// conditions: ['development'],
|
|
112
|
-
// },
|
|
113
|
-
};
|
|
114
|
-
},
|
|
115
|
-
async configureServer(server) {
|
|
116
|
-
// HACK: We know we are in dev here, this will prevent incorrect
|
|
117
|
-
// vite.config hot reloading. Will be removed when adopting env. API.
|
|
58
|
+
// MARK: 3. Dev serve middleware
|
|
59
|
+
gracileServePlugin({
|
|
60
|
+
state,
|
|
61
|
+
config,
|
|
62
|
+
logger,
|
|
63
|
+
resetClientBuiltFlag: () => {
|
|
118
64
|
isClientBuilt = false;
|
|
119
|
-
// Infos
|
|
120
|
-
// // NOTE: Beware import.meta.resolve is only compatible
|
|
121
|
-
// // with v20.6.0 (without cli flag)and upward
|
|
122
|
-
// // Not working with StackBlitz ATM?
|
|
123
|
-
// const mainPjson = import.meta
|
|
124
|
-
// .resolve('@gracile/gracile')
|
|
125
|
-
// // NOTE: Weirdly, it will assume that it's `dist/**.js`,
|
|
126
|
-
// // even after fiddling with pjson exports.
|
|
127
|
-
// .replace('/dist/index.js', '/package.json');
|
|
128
|
-
// const { version } = JSON.parse(
|
|
129
|
-
// await readFile(new URL(mainPjson), 'utf-8'),
|
|
130
|
-
// ) as {
|
|
131
|
-
// version: number;
|
|
132
|
-
// };
|
|
133
|
-
const version = getVersion();
|
|
134
|
-
logger.info(`${c.cyan(c.italic(c.underline('🧚 Gracile')))}` +
|
|
135
|
-
` ${c.dim(`~`)} ${c.green(`v${version ?? 'X'}`)}`);
|
|
136
|
-
// ---
|
|
137
|
-
const { handler } = await createDevelopmentHandler({
|
|
138
|
-
routes,
|
|
139
|
-
vite: server,
|
|
140
|
-
gracileConfig,
|
|
141
|
-
});
|
|
142
|
-
logger.info(c.dim('Vite development server is starting…'), {
|
|
143
|
-
timestamp: true,
|
|
144
|
-
});
|
|
145
|
-
server.watcher.on('ready', () => {
|
|
146
|
-
setTimeout(() => {
|
|
147
|
-
logger.info('');
|
|
148
|
-
logger.info(c.green('Watching for file changes…'), {
|
|
149
|
-
timestamp: true,
|
|
150
|
-
});
|
|
151
|
-
logger.info('');
|
|
152
|
-
// NOTE: We want it to show after the Vite intro stuff
|
|
153
|
-
}, 100);
|
|
154
|
-
});
|
|
155
|
-
return () => {
|
|
156
|
-
server.middlewares.use((request, response, next) => {
|
|
157
|
-
const locals = config?.dev?.locals?.({ nodeRequest: request });
|
|
158
|
-
Promise.resolve(nodeAdapter(handler, { logger })(request, response, locals)).catch((error) => next(error));
|
|
159
|
-
});
|
|
160
|
-
};
|
|
161
65
|
},
|
|
162
|
-
},
|
|
66
|
+
}),
|
|
67
|
+
// MARK: 4. Client virtual routes
|
|
163
68
|
virtualRoutesForClient,
|
|
69
|
+
// MARK: 5. HTML routes loader
|
|
164
70
|
htmlRoutesLoader(),
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
// NOTE: Important. Get the dev. server elements renderers.
|
|
178
|
-
gracileConfig.litSsr ??= {};
|
|
179
|
-
gracileConfig.litSsr.renderInfo = getPluginContext(viteServerForClientHtmlBuild.config)?.litSsrRenderInfo;
|
|
180
|
-
const htmlPages = await buildRoutes({
|
|
181
|
-
viteServerForBuild: viteServerForClientHtmlBuild,
|
|
182
|
-
root: viteConfig.root || process.cwd(),
|
|
183
|
-
gracileConfig,
|
|
184
|
-
serverMode: outputMode === 'server',
|
|
185
|
-
routes,
|
|
186
|
-
});
|
|
187
|
-
renderedRoutes = htmlPages.renderedRoutes;
|
|
188
|
-
await viteServerForClientHtmlBuild.close();
|
|
189
|
-
return {
|
|
190
|
-
build: {
|
|
191
|
-
// ssrManifest: true,
|
|
192
|
-
rollupOptions: {
|
|
193
|
-
input: htmlPages.inputList,
|
|
194
|
-
plugins: [htmlPages.plugin],
|
|
195
|
-
},
|
|
196
|
-
outDir: join(viteConfig.build?.outDir || 'dist', outputMode === 'server' ? 'client' : ''),
|
|
197
|
-
},
|
|
198
|
-
};
|
|
199
|
-
},
|
|
200
|
-
},
|
|
201
|
-
{
|
|
202
|
-
name: 'vite-plugin-gracile-collect-client-assets-for-server',
|
|
203
|
-
writeBundle(_, bundle) {
|
|
204
|
-
if (outputMode === 'static')
|
|
205
|
-
return;
|
|
206
|
-
for (const file of Object.values(bundle))
|
|
207
|
-
if (file.type === 'asset' && file.name)
|
|
208
|
-
clientAssets[file.name] = file.fileName;
|
|
209
|
-
},
|
|
210
|
-
},
|
|
211
|
-
{
|
|
212
|
-
name: 'vite-plugin-gracile-server-build',
|
|
213
|
-
apply: 'build',
|
|
214
|
-
config(viteConfig) {
|
|
215
|
-
root = viteConfig.root || null;
|
|
216
|
-
},
|
|
217
|
-
async closeBundle() {
|
|
218
|
-
if (outputMode === 'static' || !routes || !renderedRoutes)
|
|
219
|
-
return;
|
|
220
|
-
await build({
|
|
221
|
-
root: root || process.cwd(),
|
|
222
|
-
ssr: { external: ['@gracile/gracile'] },
|
|
223
|
-
build: {
|
|
224
|
-
target: 'esnext',
|
|
225
|
-
ssr: true,
|
|
226
|
-
// ssrManifest: true,
|
|
227
|
-
copyPublicDir: false,
|
|
228
|
-
outDir: 'dist/server',
|
|
229
|
-
ssrEmitAssets: true,
|
|
230
|
-
cssMinify: true,
|
|
231
|
-
cssCodeSplit: true,
|
|
232
|
-
rollupOptions: {
|
|
233
|
-
input: 'entrypoint.js',
|
|
234
|
-
// external: ['@gracile/gracile'],
|
|
235
|
-
// FIXME: ~~MUST import css from client somewhere.~~
|
|
236
|
-
// ~~Hack could be using dynamic imports on client, so asset is picked up~~
|
|
237
|
-
output: {
|
|
238
|
-
entryFileNames: '[name].js',
|
|
239
|
-
// assetFileNames: 'assets/[name].[ext]',
|
|
240
|
-
// NOTE: Useful for, e.g., link tag with `?url`
|
|
241
|
-
assetFileNames: (chunkInfo) => {
|
|
242
|
-
if (chunkInfo.name) {
|
|
243
|
-
const fileName = clientAssets[chunkInfo.name];
|
|
244
|
-
if (fileName)
|
|
245
|
-
return fileName;
|
|
246
|
-
// NOTE: When not imported at all from client
|
|
247
|
-
return `assets/${chunkInfo.name.replace(/\.(.*)$/, '')}-[hash].[ext]`;
|
|
248
|
-
}
|
|
249
|
-
// throw new Error(`Not a client asset`);
|
|
250
|
-
return 'assets/[name]-[hash].[ext]';
|
|
251
|
-
},
|
|
252
|
-
chunkFileNames: 'chunk/[name].js',
|
|
253
|
-
},
|
|
254
|
-
},
|
|
255
|
-
},
|
|
256
|
-
plugins: [
|
|
257
|
-
virtualRoutesForClient,
|
|
258
|
-
virtualRoutes({ routes, renderedRoutes }),
|
|
259
|
-
{
|
|
260
|
-
name: 'vite-plugin-gracile-entry',
|
|
261
|
-
resolveId(id) {
|
|
262
|
-
if (id === 'entrypoint.js') {
|
|
263
|
-
return id;
|
|
264
|
-
}
|
|
265
|
-
return null;
|
|
266
|
-
},
|
|
267
|
-
load(id) {
|
|
268
|
-
if (id === 'entrypoint.js' && routes && renderedRoutes) {
|
|
269
|
-
return `
|
|
270
|
-
import { routeAssets, routeImports, routes } from 'gracile:routes';
|
|
271
|
-
import { createGracileHandler } from '@gracile/gracile/_internals/server-runtime';
|
|
272
|
-
import { createLogger } from '@gracile/gracile/_internals/logger';
|
|
273
|
-
|
|
274
|
-
createLogger();
|
|
275
|
-
|
|
276
|
-
export const handler = createGracileHandler({
|
|
277
|
-
root: process.cwd(),
|
|
278
|
-
routes,
|
|
279
|
-
routeImports,
|
|
280
|
-
routeAssets,
|
|
281
|
-
serverMode: true,
|
|
282
|
-
gracileConfig: ${JSON.stringify(gracileConfig, null, 2)}
|
|
283
|
-
});
|
|
284
|
-
`;
|
|
285
|
-
}
|
|
286
|
-
return null;
|
|
287
|
-
},
|
|
288
|
-
},
|
|
289
|
-
{
|
|
290
|
-
name: 'gracile-move-server-assets',
|
|
291
|
-
async writeBundle(_, bundle) {
|
|
292
|
-
const cwd = root || process.cwd();
|
|
293
|
-
await Promise.all(Object.entries(bundle).map(async ([file]) => {
|
|
294
|
-
if (file.startsWith('assets/') === false)
|
|
295
|
-
return;
|
|
296
|
-
await rename(join(cwd, `/dist/server/${file}`), join(cwd, `/dist/client/${file}`));
|
|
297
|
-
}));
|
|
298
|
-
// NOTE: Disabled for now, because it conflict with test's folder comparer
|
|
299
|
-
await rm(join(cwd, `/dist/server/assets`), {
|
|
300
|
-
recursive: true,
|
|
301
|
-
}).catch(() => null);
|
|
302
|
-
},
|
|
303
|
-
},
|
|
304
|
-
],
|
|
305
|
-
});
|
|
306
|
-
},
|
|
307
|
-
},
|
|
71
|
+
// MARK: 6. Client build
|
|
72
|
+
gracileClientBuildPlugin({
|
|
73
|
+
state,
|
|
74
|
+
virtualRoutesForClient,
|
|
75
|
+
}),
|
|
76
|
+
// MARK: 7. Collect client assets for server
|
|
77
|
+
gracileCollectClientAssetsPlugin({ state }),
|
|
78
|
+
// MARK: 8. Server build (nested)
|
|
79
|
+
gracileServerBuildPlugin({
|
|
80
|
+
state,
|
|
81
|
+
virtualRoutesForClient,
|
|
82
|
+
}),
|
|
308
83
|
];
|
|
309
84
|
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, testable pipeline steps extracted from the route template renderer.
|
|
3
|
+
*
|
|
4
|
+
* Each function here is a focused stage of the document post-processing
|
|
5
|
+
* lifecycle. They are composed by `renderRouteTemplate` in
|
|
6
|
+
* `./route-template.ts`.
|
|
7
|
+
*
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
import { Readable } from 'node:stream';
|
|
11
|
+
import type { RenderInfo } from '@lit-labs/ssr';
|
|
12
|
+
export declare const REGEX_TAG_SCRIPT: RegExp;
|
|
13
|
+
export declare const REGEX_TAG_LINK: RegExp;
|
|
14
|
+
/**
|
|
15
|
+
* Concatenate multiple `Readable` streams into a single async iterable.
|
|
16
|
+
*/
|
|
17
|
+
export declare function concatStreams(...readables: Readable[]): AsyncGenerator<any, void, unknown>;
|
|
18
|
+
/**
|
|
19
|
+
* Merge user-provided `RenderInfo` with the default `LitElementRenderer`.
|
|
20
|
+
* Always appends `LitElementRenderer` to whatever the user supplied.
|
|
21
|
+
*/
|
|
22
|
+
export declare function mergeRenderInfo(renderInfo: Partial<RenderInfo> | undefined): Partial<RenderInfo>;
|
|
23
|
+
/**
|
|
24
|
+
* Inject sibling page assets into the rendered document HTML.
|
|
25
|
+
*
|
|
26
|
+
* Inserts the `PAGE_ASSETS_MARKER` before `</head>`, then replaces it
|
|
27
|
+
* with concrete `<script>` / `<link>` tags for each asset path.
|
|
28
|
+
*
|
|
29
|
+
* @param documentHtml The rendered document string.
|
|
30
|
+
* @param pageAssets Array of asset file paths (e.g. `['src/pages/about.css']`).
|
|
31
|
+
* @returns The document with asset tags injected (or unchanged if no assets).
|
|
32
|
+
*/
|
|
33
|
+
export declare function injectSiblingAssets(documentHtml: string, pageAssets: string[]): string;
|
|
34
|
+
/**
|
|
35
|
+
* Prepend `<!doctype html>` if the document doesn't already start with one.
|
|
36
|
+
*
|
|
37
|
+
* @param documentHtml The rendered document string.
|
|
38
|
+
* @returns The document guaranteed to start with a doctype declaration.
|
|
39
|
+
*/
|
|
40
|
+
export declare function ensureDoctype(documentHtml: string): string;
|
|
41
|
+
/**
|
|
42
|
+
* The HMR error overlay script injected in dev mode.
|
|
43
|
+
* Returns the raw HTML string for the `<script>` tag.
|
|
44
|
+
*/
|
|
45
|
+
export declare function developmentOverlaySnippet(): string;
|
|
46
|
+
/**
|
|
47
|
+
* Inject the dev overlay script right after `<head>`.
|
|
48
|
+
* Only applied when `mode === 'dev'`.
|
|
49
|
+
*
|
|
50
|
+
* @param documentHtml The rendered document string.
|
|
51
|
+
* @returns The document with the overlay injected.
|
|
52
|
+
*/
|
|
53
|
+
export declare function injectDevelopmentOverlay(documentHtml: string): string;
|
|
54
|
+
/**
|
|
55
|
+
* For server-output builds: strip dev-time `<script type="module">` and
|
|
56
|
+
* `<link rel="stylesheet">` tags, then inject the production asset string
|
|
57
|
+
* before `</head>`.
|
|
58
|
+
*
|
|
59
|
+
* @param documentHtml The rendered document string.
|
|
60
|
+
* @param routeAssetsHtml The production asset tags to inject.
|
|
61
|
+
* @returns The document with dev assets stripped and production assets injected.
|
|
62
|
+
*/
|
|
63
|
+
export declare function injectServerAssets(documentHtml: string, routeAssetsHtml: string): string;
|
|
64
|
+
//# sourceMappingURL=route-template-pipeline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-template-pipeline.d.ts","sourceRoot":"","sources":["../../src/render/route-template-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAIvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAMhD,eAAO,MAAM,gBAAgB,QACkC,CAAC;AAEhE,eAAO,MAAM,cAAc,QAA2B,CAAC;AAIvD;;GAEG;AACH,wBAAuB,aAAa,CAAC,GAAG,SAAS,EAAE,QAAQ,EAAE,sCAM5D;AAID;;;GAGG;AACH,wBAAgB,eAAe,CAC9B,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,SAAS,GACzC,OAAO,CAAC,UAAU,CAAC,CAQrB;AASD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAClC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAAE,GAClB,MAAM,CA2BR;AAID;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAO1D;AAID;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAalD;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAKrE;AAID;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CACjC,YAAY,EAAE,MAAM,EACpB,eAAe,EAAE,MAAM,GACrB,MAAM,CAWR"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, testable pipeline steps extracted from the route template renderer.
|
|
3
|
+
*
|
|
4
|
+
* Each function here is a focused stage of the document post-processing
|
|
5
|
+
* lifecycle. They are composed by `renderRouteTemplate` in
|
|
6
|
+
* `./route-template.ts`.
|
|
7
|
+
*
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
import { Readable } from 'node:stream';
|
|
11
|
+
import { html } from '@gracile/internal-utils/dummy-literals';
|
|
12
|
+
import { LitElementRenderer } from '@lit-labs/ssr/lib/lit-element-renderer.js';
|
|
13
|
+
import { PAGE_ASSETS_MARKER } from './markers.js';
|
|
14
|
+
// ── Regexes (re-exported for testability) ────────────────────────────
|
|
15
|
+
export const REGEX_TAG_SCRIPT = /\s?<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script\s*>\s?/gi;
|
|
16
|
+
export const REGEX_TAG_LINK = /\s?<link\b[^>]*?>\s?/gi;
|
|
17
|
+
// ── Helpers ──────────────────────────────────────────────────────────
|
|
18
|
+
/**
|
|
19
|
+
* Concatenate multiple `Readable` streams into a single async iterable.
|
|
20
|
+
*/
|
|
21
|
+
export async function* concatStreams(...readables) {
|
|
22
|
+
for (const readable of readables) {
|
|
23
|
+
for await (const chunk of readable) {
|
|
24
|
+
yield chunk;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// ── 1. Merge render info ─────────────────────────────────────────────
|
|
29
|
+
/**
|
|
30
|
+
* Merge user-provided `RenderInfo` with the default `LitElementRenderer`.
|
|
31
|
+
* Always appends `LitElementRenderer` to whatever the user supplied.
|
|
32
|
+
*/
|
|
33
|
+
export function mergeRenderInfo(renderInfo) {
|
|
34
|
+
return {
|
|
35
|
+
...renderInfo,
|
|
36
|
+
elementRenderers: [
|
|
37
|
+
...(renderInfo?.elementRenderers || []),
|
|
38
|
+
LitElementRenderer,
|
|
39
|
+
],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
// ── 2. Inject sibling page assets ───────────────────────────────────
|
|
43
|
+
/** Regex matching JS/TS asset extensions. */
|
|
44
|
+
const REGEX_SCRIPT_EXT = /\.(js|ts|jsx|tsx)$/;
|
|
45
|
+
/** Regex matching CSS-like asset extensions. */
|
|
46
|
+
const REGEX_STYLE_EXT = /\.(css|scss|sass|less|styl|stylus)$/;
|
|
47
|
+
/**
|
|
48
|
+
* Inject sibling page assets into the rendered document HTML.
|
|
49
|
+
*
|
|
50
|
+
* Inserts the `PAGE_ASSETS_MARKER` before `</head>`, then replaces it
|
|
51
|
+
* with concrete `<script>` / `<link>` tags for each asset path.
|
|
52
|
+
*
|
|
53
|
+
* @param documentHtml The rendered document string.
|
|
54
|
+
* @param pageAssets Array of asset file paths (e.g. `['src/pages/about.css']`).
|
|
55
|
+
* @returns The document with asset tags injected (or unchanged if no assets).
|
|
56
|
+
*/
|
|
57
|
+
export function injectSiblingAssets(documentHtml, pageAssets) {
|
|
58
|
+
return documentHtml
|
|
59
|
+
.replace('</head>', `\n${PAGE_ASSETS_MARKER}</head>`)
|
|
60
|
+
.replace(PAGE_ASSETS_MARKER, pageAssets.length > 0
|
|
61
|
+
? html `<!-- PAGE ASSETS -->` +
|
|
62
|
+
`${pageAssets
|
|
63
|
+
.map((path) => {
|
|
64
|
+
if (REGEX_SCRIPT_EXT.test(path)) {
|
|
65
|
+
// prettier-ignore
|
|
66
|
+
return html ` <script type="module" src="/${path}"></script>`;
|
|
67
|
+
}
|
|
68
|
+
if (REGEX_STYLE_EXT.test(path)) {
|
|
69
|
+
// prettier-ignore
|
|
70
|
+
return html ` <link rel="stylesheet" href="/${path}" />`;
|
|
71
|
+
}
|
|
72
|
+
// NOTE: Never called (filtered upstream in `collectRoutes`)
|
|
73
|
+
return null;
|
|
74
|
+
})
|
|
75
|
+
.join('\n')}` +
|
|
76
|
+
`<!-- /PAGE ASSETS -->\n `
|
|
77
|
+
: '');
|
|
78
|
+
}
|
|
79
|
+
// ── 3. Ensure doctype ────────────────────────────────────────────────
|
|
80
|
+
/**
|
|
81
|
+
* Prepend `<!doctype html>` if the document doesn't already start with one.
|
|
82
|
+
*
|
|
83
|
+
* @param documentHtml The rendered document string.
|
|
84
|
+
* @returns The document guaranteed to start with a doctype declaration.
|
|
85
|
+
*/
|
|
86
|
+
export function ensureDoctype(documentHtml) {
|
|
87
|
+
if (documentHtml.trimStart().toLocaleLowerCase().startsWith('<!doctype') ===
|
|
88
|
+
false)
|
|
89
|
+
return `<!doctype html>\n${documentHtml}`;
|
|
90
|
+
return documentHtml;
|
|
91
|
+
}
|
|
92
|
+
// ── 4. Inject dev overlay ────────────────────────────────────────────
|
|
93
|
+
/**
|
|
94
|
+
* The HMR error overlay script injected in dev mode.
|
|
95
|
+
* Returns the raw HTML string for the `<script>` tag.
|
|
96
|
+
*/
|
|
97
|
+
export function developmentOverlaySnippet() {
|
|
98
|
+
return html `
|
|
99
|
+
<script type="module">
|
|
100
|
+
if (import.meta.hot) {
|
|
101
|
+
import.meta.hot.on('gracile:ssr-error', (error) => {
|
|
102
|
+
console.error(error.message);
|
|
103
|
+
});
|
|
104
|
+
import.meta.hot.on('error', (payload) => {
|
|
105
|
+
console.error(payload.err.message);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
</script>
|
|
109
|
+
`;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Inject the dev overlay script right after `<head>`.
|
|
113
|
+
* Only applied when `mode === 'dev'`.
|
|
114
|
+
*
|
|
115
|
+
* @param documentHtml The rendered document string.
|
|
116
|
+
* @returns The document with the overlay injected.
|
|
117
|
+
*/
|
|
118
|
+
export function injectDevelopmentOverlay(documentHtml) {
|
|
119
|
+
return documentHtml.replace('<head>', `<head>\n${developmentOverlaySnippet()}`);
|
|
120
|
+
}
|
|
121
|
+
// ── 5. Inject server runtime assets ─────────────────────────────────
|
|
122
|
+
/**
|
|
123
|
+
* For server-output builds: strip dev-time `<script type="module">` and
|
|
124
|
+
* `<link rel="stylesheet">` tags, then inject the production asset string
|
|
125
|
+
* before `</head>`.
|
|
126
|
+
*
|
|
127
|
+
* @param documentHtml The rendered document string.
|
|
128
|
+
* @param routeAssetsHtml The production asset tags to inject.
|
|
129
|
+
* @returns The document with dev assets stripped and production assets injected.
|
|
130
|
+
*/
|
|
131
|
+
export function injectServerAssets(documentHtml, routeAssetsHtml) {
|
|
132
|
+
return documentHtml
|
|
133
|
+
.replaceAll(REGEX_TAG_SCRIPT, (s) => {
|
|
134
|
+
if (s.includes(`type="module"`))
|
|
135
|
+
return '';
|
|
136
|
+
return s;
|
|
137
|
+
})
|
|
138
|
+
.replaceAll(REGEX_TAG_LINK, (s) => {
|
|
139
|
+
if (s.includes(`rel="stylesheet"`))
|
|
140
|
+
return '';
|
|
141
|
+
return s;
|
|
142
|
+
})
|
|
143
|
+
.replace('</head>', `${routeAssetsHtml}\n</head>`);
|
|
144
|
+
}
|
|
@@ -3,8 +3,7 @@ import { type RenderInfo } from '@lit-labs/ssr';
|
|
|
3
3
|
import type { ViteDevServer } from 'vite';
|
|
4
4
|
import type { RouteInfos } from '../routes/match.js';
|
|
5
5
|
import type * as R from '../routes/route.js';
|
|
6
|
-
export
|
|
7
|
-
export declare const REGEX_TAG_LINK: RegExp;
|
|
6
|
+
export { REGEX_TAG_SCRIPT, REGEX_TAG_LINK } from './route-template-pipeline.js';
|
|
8
7
|
export declare function renderRouteTemplate({ url, vite, mode, routeInfos, routeAssets, serverMode, docOnly, renderInfo, }: {
|
|
9
8
|
url: string;
|
|
10
9
|
vite?: ViteDevServer | undefined;
|
|
@@ -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;
|
|
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;AAExE,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,CAwJhE"}
|