evolit 0.3.0 → 0.4.1

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/README.md CHANGED
@@ -321,6 +321,68 @@ server components and helpers that do not receive route props directly. It retur
321
321
  Reading `getRouteState().url` has the same dynamic-rendering semantics as `requestUrl()`; reading
322
322
  `params` and `searchParams` participates in the normal segment-cache key tracking.
323
323
 
324
+ ## LitSX compiler integrations
325
+
326
+ `evolit.config.js` can configure the native LitSX compiler and register build-tool-neutral LitSX
327
+ integrations. The same declaration is used by development, SSR, hydration, production builds and
328
+ the standalone runtime:
329
+
330
+ ```js
331
+ import { litsxUnoCss } from "@litsx/unocss";
332
+ import { defineEvolitConfig } from "evolit/litsx";
333
+
334
+ export default defineEvolitConfig({
335
+ litsx: {
336
+ compiler: {
337
+ sourceMaps: true,
338
+ },
339
+ integrations: [litsxUnoCss()],
340
+ },
341
+ });
342
+ ```
343
+
344
+ This is the complete Evolit + UnoCSS setup. The application does not coordinate compiler plugins,
345
+ virtual modules, generation, finalization or global CSS links, and it does not need Vite or
346
+ PostCSS. `@litsx/unocss` remains the owner of candidate extraction and CSS generation; Evolit only
347
+ executes its neutral LitSX lifecycle.
348
+
349
+ `compiler` accepts the public `TransformLitsxOptions` surface except the values Evolit must own for
350
+ correct server and browser graphs. Evolit always supplies the current `filename`, selects `ssr` per
351
+ target and forces native lowering with `reactCompat: false`; `compiler.sourceMaps` can override the
352
+ existing per-pipeline default without changing applications that omit `litsx`.
353
+ Authoring and output plugins contributed by the application and integrations are composed in
354
+ declaration order rather than replacing framework invariants.
355
+
356
+ An integration is a descriptor with a unique `name` and `create(context)` method. Each call to
357
+ `create` belongs to exactly one development server, build or standalone runtime, so mutable caches
358
+ must live on the returned instance, never on the descriptor. An instance can contribute:
359
+
360
+ - `compiler`: native compiler options, including authoring and output plugins.
361
+ - `resolveModule`: sources for integration-owned virtual modules in both server and browser graphs.
362
+ - `processModule`: post-processing, observable dependencies and preliminary outputs for a compiled
363
+ module.
364
+ - `finalize`: graph-wide assets, styles or modules after every reachable module is known.
365
+ - `invalidate` and `forget`: configuration/dependency refresh and removal of stale module state.
366
+ - `dispose`: idempotent cleanup when the owning server, build or runtime closes.
367
+
368
+ Declared outputs use stable IDs unique within the pipeline and one of `asset`, `module` or `style`. A module
369
+ may expose a safe `virtual:` specifier; a style with `document: true` is content-hashed, included in
370
+ the client asset manifest and linked once in the document. Evolit publishes a generation only after
371
+ every integration finalizes successfully, so a failed generation cannot expose a partial mix of
372
+ old and new assets.
373
+
374
+ In development, dependencies reported by compiler plugins and lifecycle hooks are watched together
375
+ with application modules. Invalidating a dependency advances the integration generation, evicts
376
+ affected compiled modules and calls `forget` for modules that leave the graph. Configuration reload
377
+ therefore removes obsolete results instead of accumulating them. Hook failures identify the
378
+ integration, lifecycle phase and relevant module in development; production errors keep the same
379
+ context while redacting project-local absolute paths.
380
+
381
+ For native Shadow DOM, `@litsx/unocss` serializes component preflight inside each Declarative Shadow
382
+ Root, preserves authored `Component.styles` before generated utilities, and emits the document
383
+ theme/custom-property layers once. Those document variables inherit through Shadow Roots and the
384
+ browser hydrates the server result without switching to `react-compat`.
385
+
324
386
  ## Server Setup
325
387
 
326
388
  Server integrations that need one-time application setup can be registered with
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "evolit",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "A convention-driven application framework for LitSX and web components.",
5
5
  "type": "module",
6
6
  "packageManager": "yarn@4.10.3",
@@ -34,6 +34,10 @@
34
34
  "browser": "./src/extensions-client.js",
35
35
  "default": "./src/extensions.js"
36
36
  },
37
+ "./litsx": {
38
+ "types": "./src/litsx-pipeline.d.ts",
39
+ "default": "./src/litsx-pipeline.js"
40
+ },
37
41
  "./internal/development-hot": "./src/development-hot-client.js"
38
42
  },
39
43
  "scripts": {
@@ -79,8 +83,10 @@
79
83
  }
80
84
  },
81
85
  "devDependencies": {
86
+ "@litsx/unocss": "1.0.0-next.8",
82
87
  "@playwright/test": "^1.62.0",
83
88
  "@webcomponents/scoped-custom-element-registry": "^0.0.10",
89
+ "unocss": "66.8.1",
84
90
  "vite": "^8.1.5"
85
91
  }
86
92
  }
package/src/build.js CHANGED
@@ -58,6 +58,7 @@ import {
58
58
  runWithOptionalSsrUrqlScope,
59
59
  } from "./urql-ssr.js";
60
60
  import { runServerSetup } from "./server-setup.js";
61
+ import { createLitsxPipeline, mergeLitsxAssetsIntoManifest } from "./litsx-pipeline.js";
61
62
 
62
63
  const CONTENT_TYPE_BY_EXTENSION = new Map([
63
64
  [".css", "text/css; charset=utf-8"],
@@ -163,11 +164,20 @@ async function writeDeploymentRuntimeEntry(buildRoot) {
163
164
 
164
165
  export async function buildProject(projectRoot, options = {}) {
165
166
  const evolitConfig = await loadEvolitConfig(projectRoot);
166
- const serverSetupCleanup = await runServerSetup({
167
+ const litsxPipeline = await createLitsxPipeline({
167
168
  projectRoot,
168
169
  mode: "production",
169
- evolitConfig,
170
+ config: evolitConfig,
170
171
  });
172
+ let serverSetupCleanup = null;
173
+ let buildError = null;
174
+ try {
175
+ serverSetupCleanup = await runServerSetup({
176
+ projectRoot,
177
+ mode: "production",
178
+ evolitConfig,
179
+ litsxPipeline,
180
+ });
171
181
  const extensions = resolveEvolitExtensions(evolitConfig);
172
182
  const extensionClientDescriptors = getExtensionClientDescriptors(extensions);
173
183
  const packageClientSpecifiers = new Set(
@@ -194,6 +204,7 @@ export async function buildProject(projectRoot, options = {}) {
194
204
  sourceMaps: false,
195
205
  ssr: true,
196
206
  target: "server",
207
+ litsxPipeline,
197
208
  });
198
209
  result.packageImports.forEach((specifier) => ssrPackageImports.add(specifier));
199
210
  return result;
@@ -202,7 +213,7 @@ export async function buildProject(projectRoot, options = {}) {
202
213
  function getEntryInventory(entryPath) {
203
214
  let inventory = inventoriesBySourceEntry.get(entryPath);
204
215
  if (!inventory) {
205
- inventory = collectClientGraphInventory([entryPath], { projectRoot });
216
+ inventory = collectClientGraphInventory([entryPath], { projectRoot, litsxPipeline });
206
217
  inventoriesBySourceEntry.set(entryPath, inventory);
207
218
  }
208
219
  return inventory;
@@ -216,6 +227,7 @@ export async function buildProject(projectRoot, options = {}) {
216
227
  mode: "production",
217
228
  sourceMaps: true,
218
229
  target: "client",
230
+ litsxPipeline,
219
231
  });
220
232
  clientModule = path.relative(clientBuild.outputRoot, clientBuild.entrypoint)
221
233
  .split(path.sep)
@@ -235,6 +247,7 @@ export async function buildProject(projectRoot, options = {}) {
235
247
  mode: "production",
236
248
  ssr: true,
237
249
  target: "server",
250
+ litsxPipeline,
238
251
  });
239
252
  const methods = Object.keys(handlerModule)
240
253
  .filter((name) => /^[A-Z]+$/.test(name) && typeof handlerModule[name] === "function")
@@ -345,18 +358,20 @@ export async function buildProject(projectRoot, options = {}) {
345
358
  }
346
359
 
347
360
  sharedVendorOptions.additionalEntrySpecifiers = [...packageClientSpecifiers].sort();
348
- const clientAssets = await emitBundledClientAssets(projectRoot, {
361
+ let clientAssets = await emitBundledClientAssets(projectRoot, {
349
362
  entryClientModules,
350
363
  additionalVendorSpecifiers: sharedVendorOptions.additionalEntrySpecifiers,
351
364
  serverAssetImportsByEntry,
352
365
  clientBoundariesByEntry,
353
366
  });
367
+ const integrationOutputs = await litsxPipeline?.finalize({
368
+ routes,
369
+ routeHandlers,
370
+ });
371
+ clientAssets = mergeLitsxAssetsIntoManifest(clientAssets, integrationOutputs);
354
372
  const staticAssetPublicUrls = createStaticAssetPublicUrlMap(clientAssets);
355
373
  await rewriteServerAssetPlaceholders(projectRoot, clientAssets);
356
374
 
357
- const routeResolver = await createRouteResolver(projectRoot, "production", {
358
- staticAssetPublicUrls,
359
- });
360
375
  const sharedRuntime = await buildSharedVendorRuntime(projectRoot, "production", sharedVendorOptions);
361
376
  clientAssets.sharedImports = { ...sharedRuntime.imports };
362
377
  const packageImports = sharedRuntime.imports;
@@ -364,6 +379,11 @@ export async function buildProject(projectRoot, options = {}) {
364
379
  assetManifest: clientAssets,
365
380
  packageImports,
366
381
  });
382
+ const routeResolver = await createRouteResolver(projectRoot, "production", {
383
+ staticAssetPublicUrls,
384
+ assetResolver,
385
+ litsxPipeline,
386
+ });
367
387
  const hydrationModuleUrl = await resolveSharedVendorModuleUrl(
368
388
  projectRoot,
369
389
  "production",
@@ -423,7 +443,21 @@ export async function buildProject(projectRoot, options = {}) {
423
443
  }
424
444
  continue;
425
445
  }
426
- if (assetResolver(moduleId) || clientAssets.byPublicPath?.[moduleId]) continue;
446
+ const directPublicUrl = assetResolver(moduleId)
447
+ ?? (clientAssets.byPublicPath?.[moduleId] ? moduleId : null);
448
+ if (directPublicUrl) {
449
+ if (Array.isArray(result.clientImports)) {
450
+ result.clientImports = result.clientImports.map((value) => (
451
+ value === moduleId ? directPublicUrl : value
452
+ ));
453
+ }
454
+ if (Array.isArray(result.hydrationData?.clientImports)) {
455
+ result.hydrationData.clientImports = result.hydrationData.clientImports.map((value) => (
456
+ value === moduleId ? directPublicUrl : value
457
+ ));
458
+ }
459
+ continue;
460
+ }
427
461
  const importerPath = routeResult.boundaryModule
428
462
  ?? routeResult.route?.page
429
463
  ?? path.join(projectRoot, "app", "page.jsx");
@@ -473,6 +507,7 @@ export async function buildProject(projectRoot, options = {}) {
473
507
  ])]
474
508
  : [];
475
509
  const styleUrls = [...new Set([
510
+ ...(clientAssets.documentStyles ?? []),
476
511
  ...collectTransitiveStyleUrls(clientImports, clientAssets),
477
512
  ...resolveServerStyleUrls(routeResult, projectRoot, clientAssets),
478
513
  ])];
@@ -650,6 +685,11 @@ export async function buildProject(projectRoot, options = {}) {
650
685
  cache: sortedRouteCache.find((entry) => entry.pathname === artifact.routePathname)?.cache
651
686
  ?? "static",
652
687
  })),
688
+ ...(integrationOutputs?.assets ?? []).map((asset) => ({
689
+ kind: asset.kind,
690
+ outputPath: toBuildRelativePath(projectRoot, asset.outputPath),
691
+ integration: asset.integration,
692
+ })),
653
693
  ],
654
694
  };
655
695
  const manifestPath = path.join(buildRoot, MANIFEST_FILENAME);
@@ -676,7 +716,22 @@ export async function buildProject(projectRoot, options = {}) {
676
716
  await writeJson(path.join(buildRoot, DEPLOY_ASSETS_MANIFEST_FILENAME), deployAssets);
677
717
  await writeJson(path.join(buildRoot, DEPLOY_SERVER_MANIFEST_FILENAME), deployServer);
678
718
 
679
- await serverSetupCleanup?.();
680
-
681
719
  return manifestPath;
720
+ } catch (error) {
721
+ buildError = error;
722
+ throw error;
723
+ } finally {
724
+ let cleanupError = null;
725
+ try {
726
+ await serverSetupCleanup?.();
727
+ } catch (error) {
728
+ cleanupError = error;
729
+ }
730
+ try {
731
+ await litsxPipeline?.dispose();
732
+ } catch (error) {
733
+ cleanupError ??= error;
734
+ }
735
+ if (!buildError && cleanupError) throw cleanupError;
736
+ }
682
737
  }
@@ -2483,6 +2483,7 @@ export function normalizeClientAssetManifest(manifest) {
2483
2483
  manifest.clientBoundariesByEntry && typeof manifest.clientBoundariesByEntry === "object"
2484
2484
  ? manifest.clientBoundariesByEntry
2485
2485
  : {},
2486
+ documentStyles: Array.isArray(manifest.documentStyles) ? manifest.documentStyles : [],
2486
2487
  assets: manifest.assets,
2487
2488
  };
2488
2489
  }
package/src/compiler.js CHANGED
@@ -182,14 +182,28 @@ async function transformModuleSource(source, {
182
182
  sourcePath,
183
183
  sourceMaps,
184
184
  ssr = false,
185
+ target = ssr ? "server" : "client",
186
+ litsxPipeline = null,
185
187
  }) {
186
188
  if (isLitsxAuthoredModule(sourcePath, source)) {
187
- const transformed = await transformLitsx(source, {
188
- filename: sourcePath,
189
- sourceMaps,
190
- ssr,
191
- });
192
- return restoreSideEffectImports(source, sourcePath, transformed, sourceMaps);
189
+ const compilerOptions = litsxPipeline
190
+ ? litsxPipeline.getCompilerOptions({ filename: sourcePath, sourceMaps, ssr })
191
+ : { filename: sourcePath, sourceMaps, ssr };
192
+ const transformed = restoreSideEffectImports(
193
+ source,
194
+ sourcePath,
195
+ await transformLitsx(source, compilerOptions),
196
+ compilerOptions.sourceMaps === true,
197
+ );
198
+ return litsxPipeline
199
+ ? litsxPipeline.processModule(transformed, {
200
+ source,
201
+ sourcePath,
202
+ sourceMaps: compilerOptions.sourceMaps === true,
203
+ ssr: ssr === true,
204
+ target,
205
+ })
206
+ : transformed;
193
207
  }
194
208
 
195
209
  if (sourcePath.endsWith(".ts")) {
@@ -914,8 +928,9 @@ function getTypedOutputRoot(projectRoot, mode, target = "server") {
914
928
  );
915
929
  }
916
930
 
917
- function toOutputPath(projectRoot, outputRoot, sourcePath) {
918
- const relativePath = toOutputRelativePath(projectRoot, sourcePath);
931
+ function toOutputPath(projectRoot, outputRoot, sourcePath, litsxPipeline = null) {
932
+ const relativePath = litsxPipeline?.getOutputRelativePath?.(sourcePath)
933
+ ?? toOutputRelativePath(projectRoot, sourcePath);
919
934
  const extension = path.extname(relativePath);
920
935
  if (!extension) {
921
936
  return path.join(outputRoot, `${relativePath}.mjs`);
@@ -963,6 +978,7 @@ async function rewriteRelativeSpecifiers({
963
978
  serverExportsByModule,
964
979
  packageImports,
965
980
  dynamicImportDirectories,
981
+ litsxPipeline,
966
982
  }) {
967
983
  const magicSource = new MagicString(code);
968
984
  let didRewrite = false;
@@ -974,10 +990,19 @@ async function rewriteRelativeSpecifiers({
974
990
  for (const reference of moduleReferences.staticReferences) {
975
991
  const { specifier } = reference;
976
992
 
977
- const aliasedImportPath = isBareSpecifier(specifier)
993
+ const virtualImportPath = litsxPipeline
994
+ ? await litsxPipeline.resolveModule(specifier, {
995
+ importer: sourcePath,
996
+ target,
997
+ ssr: target === "server",
998
+ sourceMaps,
999
+ })
1000
+ : null;
1001
+
1002
+ const aliasedImportPath = !virtualImportPath && isBareSpecifier(specifier)
978
1003
  ? await resolveProjectMappedImport(projectRoot, sourcePath, specifier)
979
1004
  : null;
980
- const packageImportPath = isBareSpecifier(specifier) && !aliasedImportPath
1005
+ const packageImportPath = !virtualImportPath && isBareSpecifier(specifier) && !aliasedImportPath
981
1006
  ? await resolveProjectPackageImport(projectRoot, sourcePath, specifier)
982
1007
  : null;
983
1008
  const packageAssetPath = packageImportPath && isStaticAssetPath(packageImportPath)
@@ -987,6 +1012,7 @@ async function rewriteRelativeSpecifiers({
987
1012
  if (
988
1013
  target === "server"
989
1014
  && isBareSpecifier(specifier)
1015
+ && !virtualImportPath
990
1016
  && !aliasedImportPath
991
1017
  && !packageAssetPath
992
1018
  ) {
@@ -996,6 +1022,7 @@ async function rewriteRelativeSpecifiers({
996
1022
  if (
997
1023
  target === "client"
998
1024
  && isBareSpecifier(specifier)
1025
+ && !virtualImportPath
999
1026
  && !aliasedImportPath
1000
1027
  && !packageAssetPath
1001
1028
  ) {
@@ -1010,11 +1037,12 @@ async function rewriteRelativeSpecifiers({
1010
1037
  continue;
1011
1038
  }
1012
1039
 
1013
- if (!isRelativeSpecifier(specifier) && !aliasedImportPath && !packageAssetPath) {
1040
+ if (!virtualImportPath && !isRelativeSpecifier(specifier) && !aliasedImportPath && !packageAssetPath) {
1014
1041
  continue;
1015
1042
  }
1016
1043
 
1017
- const resolvedImportPath = packageAssetPath
1044
+ const resolvedImportPath = virtualImportPath
1045
+ ?? packageAssetPath
1018
1046
  ?? aliasedImportPath
1019
1047
  ?? await resolveImportPath(sourcePath, specifier);
1020
1048
  if (!resolvedImportPath) {
@@ -1033,6 +1061,9 @@ async function rewriteRelativeSpecifiers({
1033
1061
 
1034
1062
  if (shouldCompileModule(resolvedImportPath)) {
1035
1063
  compiledImportPath = await compileModule(resolvedImportPath);
1064
+ if (virtualImportPath) {
1065
+ litsxPipeline?.registerResolvedModule(specifier, resolvedImportPath, compiledImportPath);
1066
+ }
1036
1067
  const serverExports = serverExportsByModule?.get(resolvedImportPath) ?? new Set();
1037
1068
  const importedNames = importedNamesBySpecifier.get(specifier) ?? new Set();
1038
1069
  if (
@@ -1245,6 +1276,7 @@ function getDevelopmentGraphCacheKey(entryPath, options) {
1245
1276
  options.target ?? "server",
1246
1277
  options.ssr === true ? "ssr" : "client",
1247
1278
  options.sourceMaps === false ? "without-maps" : "with-maps",
1279
+ options.litsxPipeline?.identity?.id ?? "default-litsx",
1248
1280
  ].join("::");
1249
1281
  }
1250
1282
 
@@ -1358,6 +1390,7 @@ async function compileModuleGraphUncached(entryPath, options = {}) {
1358
1390
  staticAssetPublicUrls = null,
1359
1391
  onDevelopmentEvent,
1360
1392
  managedSourceRoots,
1393
+ litsxPipeline,
1361
1394
  } = options;
1362
1395
 
1363
1396
  const outputRoot = getTypedOutputRoot(projectRoot, mode, target);
@@ -1384,7 +1417,7 @@ async function compileModuleGraphUncached(entryPath, options = {}) {
1384
1417
  return visited.get(sourcePath);
1385
1418
  }
1386
1419
 
1387
- const outputPath = toOutputPath(projectRoot, outputRoot, sourcePath);
1420
+ const outputPath = toOutputPath(projectRoot, outputRoot, sourcePath, litsxPipeline);
1388
1421
  visited.set(sourcePath, outputPath);
1389
1422
 
1390
1423
  await ensureDirectory(path.dirname(outputPath));
@@ -1394,6 +1427,8 @@ async function compileModuleGraphUncached(entryPath, options = {}) {
1394
1427
  sourcePath,
1395
1428
  sourceMaps,
1396
1429
  ssr,
1430
+ target,
1431
+ litsxPipeline,
1397
1432
  });
1398
1433
  const isServerComponentModule = isCompiledServerComponentModule(transformed.code);
1399
1434
  const isMixedComponentModule = isServerComponentModule
@@ -1439,6 +1474,7 @@ async function compileModuleGraphUncached(entryPath, options = {}) {
1439
1474
  serverExportsByModule,
1440
1475
  packageImports,
1441
1476
  dynamicImportDirectories,
1477
+ litsxPipeline,
1442
1478
  });
1443
1479
 
1444
1480
  await fs.writeFile(outputPath, rewritten.code, "utf8");
@@ -1467,7 +1503,12 @@ async function compileModuleGraphUncached(entryPath, options = {}) {
1467
1503
  return {
1468
1504
  entrypoint: await compileModule(entryPath),
1469
1505
  outputRoot,
1470
- sourceFiles: [...new Set([...visited.keys(), ...staticAssetFiles])],
1506
+ sourceFiles: [...new Set([
1507
+ ...visited.keys(),
1508
+ ...staticAssetFiles,
1509
+ ...(litsxPipeline?.dependencies ?? []),
1510
+ ])],
1511
+ integrationDependencies: litsxPipeline?.dependencies ?? [],
1471
1512
  watchedDirectories: [...dynamicImportDirectories],
1472
1513
  packageImports: [...packageImports].sort(),
1473
1514
  };
@@ -1625,6 +1666,8 @@ export async function collectClientGraphInventory(entryPaths, options = {}) {
1625
1666
  projectRoot,
1626
1667
  sourcePath,
1627
1668
  sourceMaps: false,
1669
+ target: "client",
1670
+ litsxPipeline: options.litsxPipeline,
1628
1671
  });
1629
1672
  const isServer = isCompiledServerComponentModule(transformed.code);
1630
1673
  const isComponent = isCompiledClientBoundaryModule(transformed.code);
@@ -53,6 +53,7 @@ import {
53
53
  runRequestExtensions,
54
54
  } from "./extensions.js";
55
55
  import { runServerSetup } from "./server-setup.js";
56
+ import { createLitsxPipeline, mergeLitsxAssetsIntoManifest } from "./litsx-pipeline.js";
56
57
 
57
58
  function isBarePackageModuleId(value) {
58
59
  return typeof value === "string"
@@ -259,6 +260,7 @@ export async function createRequestRenderer({
259
260
  onDevelopmentEvent,
260
261
  managedSourceRoots,
261
262
  extensions = [],
263
+ litsxPipeline = null,
262
264
  }) {
263
265
  let currentAssetManifest = normalizeClientAssetManifest(assetManifest);
264
266
  const extensionClientDescriptors = getExtensionClientDescriptors(extensions);
@@ -325,7 +327,11 @@ export async function createRequestRenderer({
325
327
  function getDevelopmentEntryInventory(entryPath) {
326
328
  let inventory = devInventoryByEntry.get(entryPath);
327
329
  if (!inventory) {
328
- inventory = collectClientGraphInventory([entryPath], { projectRoot, managedSourceRoots });
330
+ inventory = collectClientGraphInventory([entryPath], {
331
+ projectRoot,
332
+ managedSourceRoots,
333
+ litsxPipeline,
334
+ });
329
335
  devInventoryByEntry.set(entryPath, inventory);
330
336
  inventory.catch(() => {
331
337
  if (devInventoryByEntry.get(entryPath) === inventory) devInventoryByEntry.delete(entryPath);
@@ -337,6 +343,7 @@ export async function createRequestRenderer({
337
343
  const resolver = await createRouteResolver(projectRoot, mode, {
338
344
  onDevelopmentEvent,
339
345
  managedSourceRoots,
346
+ litsxPipeline,
340
347
  segmentRenderCache: sharedSegmentRenderCache,
341
348
  getStaticAssetPublicUrls() {
342
349
  return createStaticAssetPublicUrlMap(currentAssetManifest);
@@ -359,6 +366,7 @@ export async function createRequestRenderer({
359
366
  target: "client",
360
367
  onDevelopmentEvent,
361
368
  managedSourceRoots,
369
+ litsxPipeline,
362
370
  });
363
371
  devPreparedClientModules.add(clientModuleKey);
364
372
  devClientModuleDependencies.set(
@@ -387,7 +395,13 @@ export async function createRequestRenderer({
387
395
  clientBoundariesByEntry: devClientBoundariesByEntry,
388
396
  additionalVendorSpecifiers: [...packageClientSpecifiers],
389
397
  });
390
- currentAssetManifest = bundledClientAssets.manifest;
398
+ const integrationOutputs = await litsxPipeline?.finalize({
399
+ assetManifest: bundledClientAssets.manifest,
400
+ });
401
+ currentAssetManifest = mergeLitsxAssetsIntoManifest(
402
+ bundledClientAssets.manifest,
403
+ integrationOutputs,
404
+ );
391
405
  devPreviousAssetOutputPaths = new Set();
392
406
  currentAssetResolver = createAssetResolver(projectRoot, {
393
407
  assetManifest: currentAssetManifest,
@@ -625,6 +639,7 @@ export async function createRequestRenderer({
625
639
  : [];
626
640
  const styleUrls = currentAssetManifest
627
641
  ? [...new Set([
642
+ ...(currentAssetManifest.documentStyles ?? []),
628
643
  ...collectTransitiveStyleUrls(clientImports, currentAssetManifest),
629
644
  ...resolveServerStyleUrls(routeResult, projectRoot, currentAssetManifest),
630
645
  ])]
@@ -857,6 +872,7 @@ export async function createRequestRenderer({
857
872
  target: "client",
858
873
  onDevelopmentEvent,
859
874
  managedSourceRoots,
875
+ litsxPipeline,
860
876
  });
861
877
  }
862
878
  return;
@@ -936,7 +952,10 @@ export async function createDeploymentRuntime({
936
952
  evolitConfig: configuredEvolitConfig,
937
953
  } = {}) {
938
954
  const evolitConfig = configuredEvolitConfig ?? await loadEvolitConfig(projectRoot);
939
- const serverSetupCleanup = await runServerSetup({ projectRoot, mode, evolitConfig });
955
+ const litsxPipeline = await createLitsxPipeline({ projectRoot, mode, config: evolitConfig });
956
+ let serverSetupCleanup = null;
957
+ try {
958
+ serverSetupCleanup = await runServerSetup({ projectRoot, mode, evolitConfig, litsxPipeline });
940
959
  const extensions = resolveEvolitExtensions(evolitConfig);
941
960
  const extensionClientSpecifiers = getExtensionClientDescriptors(extensions).map((descriptor) => descriptor.module);
942
961
  const effectiveManagedSourceRoots = managedSourceRoots
@@ -994,18 +1013,26 @@ export async function createDeploymentRuntime({
994
1013
  onDevelopmentEvent,
995
1014
  managedSourceRoots: effectiveManagedSourceRoots,
996
1015
  extensions,
1016
+ litsxPipeline,
997
1017
  });
1018
+ let closed = false;
998
1019
 
999
1020
  return {
1000
1021
  assets,
1001
1022
  cache: renderer.responseCacheController,
1002
1023
  renderer,
1024
+ get litsxDependencies() {
1025
+ return litsxPipeline?.dependencies ?? [];
1026
+ },
1003
1027
  async invalidateDevelopmentState(changedPaths = null) {
1004
1028
  if (mode !== "development") {
1005
1029
  return;
1006
1030
  }
1007
1031
 
1008
- const invalidationResult = await renderer.invalidateDevelopmentState(changedPaths);
1032
+ const litsxInvalidated = await litsxPipeline?.invalidate(changedPaths);
1033
+ const invalidationResult = await renderer.invalidateDevelopmentState(
1034
+ litsxInvalidated ? null : changedPaths,
1035
+ );
1009
1036
  if (typeof effectiveResponseCacheRuntime.store.clear === "function") {
1010
1037
  await effectiveResponseCacheRuntime.store.clear();
1011
1038
  }
@@ -1075,6 +1102,7 @@ export async function createDeploymentRuntime({
1075
1102
  const { routeResult, response } = await renderer.renderRoute(resolvedRequest, routePolicyResult, {
1076
1103
  extensionState: extensionResult,
1077
1104
  });
1105
+ await litsxPipeline?.finalize({ routeResult, assetManifest: renderer.assetManifest });
1078
1106
  runtimeState.assetManifest = normalizeClientAssetManifest(renderer.assetManifest);
1079
1107
  return formatResponseForRequest(
1080
1108
  request,
@@ -1082,13 +1110,39 @@ export async function createDeploymentRuntime({
1082
1110
  );
1083
1111
  },
1084
1112
  async close() {
1113
+ if (closed) return;
1114
+ closed = true;
1115
+ let cleanupError = null;
1085
1116
  const revalidationTasks = this.revalidationTasks;
1086
1117
  if (revalidationTasks) {
1087
- await Promise.all(revalidationTasks.values());
1118
+ try {
1119
+ await Promise.all(revalidationTasks.values());
1120
+ } catch (error) {
1121
+ cleanupError = error;
1122
+ }
1088
1123
  }
1089
- await serverSetupCleanup?.();
1124
+ try {
1125
+ await serverSetupCleanup?.();
1126
+ } catch (error) {
1127
+ cleanupError = error;
1128
+ }
1129
+ try {
1130
+ await litsxPipeline?.dispose();
1131
+ } catch (error) {
1132
+ cleanupError ??= error;
1133
+ }
1134
+ if (cleanupError) throw cleanupError;
1090
1135
  },
1091
1136
  };
1137
+ } catch (error) {
1138
+ try {
1139
+ await serverSetupCleanup?.();
1140
+ } catch {}
1141
+ try {
1142
+ await litsxPipeline?.dispose();
1143
+ } catch {}
1144
+ throw error;
1145
+ }
1092
1146
  }
1093
1147
 
1094
1148
  function addNavigationVaryHeader(response) {
package/src/index.js CHANGED
@@ -124,6 +124,9 @@ export { getRequestContext } from "./request-context.js";
124
124
  /** Defines an Evolit extension for use in evolit.config.js. */
125
125
  export { defineEvolitPlugin } from "./extensions.js";
126
126
 
127
+ /** Defines a typed Evolit configuration, including the native LitSX pipeline. */
128
+ export { defineEvolitConfig, defineLitsxIntegration } from "./litsx-pipeline.js";
129
+
127
130
  /**
128
131
  * Returns the incoming request URL. Reading it makes the current route dynamic.
129
132
  *