evolit 0.2.1 → 0.4.0

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.
@@ -41,12 +41,19 @@ import {
41
41
  } from "./response-cache.js";
42
42
  import { createSsrAdapter, renderRouteTreeWithAdapter } from "./ssr-adapter.js";
43
43
  import { createNavigationResponseFromDocument } from "./route-segments.js";
44
- import { appendSsrUrqlData, runWithOptionalSsrUrqlScope } from "./urql-ssr.js";
44
+ import {
45
+ appendSsrUrqlData,
46
+ applySsrUrqlRequestContext,
47
+ createSsrUrqlRequestContext,
48
+ runWithOptionalSsrUrqlScope,
49
+ } from "./urql-ssr.js";
45
50
  import {
46
51
  getExtensionClientDescriptors,
47
52
  resolveEvolitExtensions,
48
53
  runRequestExtensions,
49
54
  } from "./extensions.js";
55
+ import { runServerSetup } from "./server-setup.js";
56
+ import { createLitsxPipeline, mergeLitsxAssetsIntoManifest } from "./litsx-pipeline.js";
50
57
 
51
58
  function isBarePackageModuleId(value) {
52
59
  return typeof value === "string"
@@ -253,6 +260,7 @@ export async function createRequestRenderer({
253
260
  onDevelopmentEvent,
254
261
  managedSourceRoots,
255
262
  extensions = [],
263
+ litsxPipeline = null,
256
264
  }) {
257
265
  let currentAssetManifest = normalizeClientAssetManifest(assetManifest);
258
266
  const extensionClientDescriptors = getExtensionClientDescriptors(extensions);
@@ -319,7 +327,11 @@ export async function createRequestRenderer({
319
327
  function getDevelopmentEntryInventory(entryPath) {
320
328
  let inventory = devInventoryByEntry.get(entryPath);
321
329
  if (!inventory) {
322
- inventory = collectClientGraphInventory([entryPath], { projectRoot, managedSourceRoots });
330
+ inventory = collectClientGraphInventory([entryPath], {
331
+ projectRoot,
332
+ managedSourceRoots,
333
+ litsxPipeline,
334
+ });
323
335
  devInventoryByEntry.set(entryPath, inventory);
324
336
  inventory.catch(() => {
325
337
  if (devInventoryByEntry.get(entryPath) === inventory) devInventoryByEntry.delete(entryPath);
@@ -331,6 +343,7 @@ export async function createRequestRenderer({
331
343
  const resolver = await createRouteResolver(projectRoot, mode, {
332
344
  onDevelopmentEvent,
333
345
  managedSourceRoots,
346
+ litsxPipeline,
334
347
  segmentRenderCache: sharedSegmentRenderCache,
335
348
  getStaticAssetPublicUrls() {
336
349
  return createStaticAssetPublicUrlMap(currentAssetManifest);
@@ -353,6 +366,7 @@ export async function createRequestRenderer({
353
366
  target: "client",
354
367
  onDevelopmentEvent,
355
368
  managedSourceRoots,
369
+ litsxPipeline,
356
370
  });
357
371
  devPreparedClientModules.add(clientModuleKey);
358
372
  devClientModuleDependencies.set(
@@ -381,7 +395,13 @@ export async function createRequestRenderer({
381
395
  clientBoundariesByEntry: devClientBoundariesByEntry,
382
396
  additionalVendorSpecifiers: [...packageClientSpecifiers],
383
397
  });
384
- currentAssetManifest = bundledClientAssets.manifest;
398
+ const integrationOutputs = await litsxPipeline?.finalize({
399
+ assetManifest: bundledClientAssets.manifest,
400
+ });
401
+ currentAssetManifest = mergeLitsxAssetsIntoManifest(
402
+ bundledClientAssets.manifest,
403
+ integrationOutputs,
404
+ );
385
405
  devPreviousAssetOutputPaths = new Set();
386
406
  currentAssetResolver = createAssetResolver(projectRoot, {
387
407
  assetManifest: currentAssetManifest,
@@ -619,6 +639,7 @@ export async function createRequestRenderer({
619
639
  : [];
620
640
  const styleUrls = currentAssetManifest
621
641
  ? [...new Set([
642
+ ...(currentAssetManifest.documentStyles ?? []),
622
643
  ...collectTransitiveStyleUrls(clientImports, currentAssetManifest),
623
644
  ...resolveServerStyleUrls(routeResult, projectRoot, currentAssetManifest),
624
645
  ])]
@@ -851,6 +872,7 @@ export async function createRequestRenderer({
851
872
  target: "client",
852
873
  onDevelopmentEvent,
853
874
  managedSourceRoots,
875
+ litsxPipeline,
854
876
  });
855
877
  }
856
878
  return;
@@ -882,7 +904,8 @@ export async function createRequestRenderer({
882
904
  return effectiveRouteResolver.resolveRoutePolicy(request, options);
883
905
  },
884
906
  async renderRoute(request, routePolicyResult = null, options = {}) {
885
- return runWithOptionalSsrUrqlScope(async (urqlAdapter) => {
907
+ const urqlRequestContext = createSsrUrqlRequestContext(request);
908
+ return runWithOptionalSsrUrqlScope(urqlRequestContext, async (urqlAdapter) => {
886
909
  const shouldPrepareBeforeResolve = mode === "development" && !currentAssetManifest;
887
910
  let resolvedRoutePolicyResult = routePolicyResult;
888
911
  if (shouldPrepareBeforeResolve) {
@@ -895,9 +918,14 @@ export async function createRequestRenderer({
895
918
  await this.prepareRouteClientArtifacts(routeResult);
896
919
  }
897
920
  const renderedResponse = await renderRouteTreeWithAdapter(routeResult, ssrAdapter);
898
- const response = urqlAdapter
921
+ const responseWithData = urqlAdapter
899
922
  ? appendSsrUrqlData(renderedResponse, await urqlAdapter.getUrqlSsrData())
900
923
  : renderedResponse;
924
+ const response = applySsrUrqlRequestContext(
925
+ routeResult,
926
+ responseWithData,
927
+ urqlRequestContext,
928
+ );
901
929
  return {
902
930
  routeResult,
903
931
  response,
@@ -921,8 +949,13 @@ export async function createDeploymentRuntime({
921
949
  routeResolver,
922
950
  onDevelopmentEvent,
923
951
  managedSourceRoots,
952
+ evolitConfig: configuredEvolitConfig,
924
953
  } = {}) {
925
- const evolitConfig = await loadEvolitConfig(projectRoot);
954
+ const evolitConfig = configuredEvolitConfig ?? await loadEvolitConfig(projectRoot);
955
+ const litsxPipeline = await createLitsxPipeline({ projectRoot, mode, config: evolitConfig });
956
+ let serverSetupCleanup = null;
957
+ try {
958
+ serverSetupCleanup = await runServerSetup({ projectRoot, mode, evolitConfig, litsxPipeline });
926
959
  const extensions = resolveEvolitExtensions(evolitConfig);
927
960
  const extensionClientSpecifiers = getExtensionClientDescriptors(extensions).map((descriptor) => descriptor.module);
928
961
  const effectiveManagedSourceRoots = managedSourceRoots
@@ -980,18 +1013,26 @@ export async function createDeploymentRuntime({
980
1013
  onDevelopmentEvent,
981
1014
  managedSourceRoots: effectiveManagedSourceRoots,
982
1015
  extensions,
1016
+ litsxPipeline,
983
1017
  });
1018
+ let closed = false;
984
1019
 
985
1020
  return {
986
1021
  assets,
987
1022
  cache: renderer.responseCacheController,
988
1023
  renderer,
1024
+ get litsxDependencies() {
1025
+ return litsxPipeline?.dependencies ?? [];
1026
+ },
989
1027
  async invalidateDevelopmentState(changedPaths = null) {
990
1028
  if (mode !== "development") {
991
1029
  return;
992
1030
  }
993
1031
 
994
- const invalidationResult = await renderer.invalidateDevelopmentState(changedPaths);
1032
+ const litsxInvalidated = await litsxPipeline?.invalidate(changedPaths);
1033
+ const invalidationResult = await renderer.invalidateDevelopmentState(
1034
+ litsxInvalidated ? null : changedPaths,
1035
+ );
995
1036
  if (typeof effectiveResponseCacheRuntime.store.clear === "function") {
996
1037
  await effectiveResponseCacheRuntime.store.clear();
997
1038
  }
@@ -1061,6 +1102,7 @@ export async function createDeploymentRuntime({
1061
1102
  const { routeResult, response } = await renderer.renderRoute(resolvedRequest, routePolicyResult, {
1062
1103
  extensionState: extensionResult,
1063
1104
  });
1105
+ await litsxPipeline?.finalize({ routeResult, assetManifest: renderer.assetManifest });
1064
1106
  runtimeState.assetManifest = normalizeClientAssetManifest(renderer.assetManifest);
1065
1107
  return formatResponseForRequest(
1066
1108
  request,
@@ -1068,12 +1110,39 @@ export async function createDeploymentRuntime({
1068
1110
  );
1069
1111
  },
1070
1112
  async close() {
1113
+ if (closed) return;
1114
+ closed = true;
1115
+ let cleanupError = null;
1071
1116
  const revalidationTasks = this.revalidationTasks;
1072
1117
  if (revalidationTasks) {
1073
- await Promise.all(revalidationTasks.values());
1118
+ try {
1119
+ await Promise.all(revalidationTasks.values());
1120
+ } catch (error) {
1121
+ cleanupError = error;
1122
+ }
1123
+ }
1124
+ try {
1125
+ await serverSetupCleanup?.();
1126
+ } catch (error) {
1127
+ cleanupError = error;
1074
1128
  }
1129
+ try {
1130
+ await litsxPipeline?.dispose();
1131
+ } catch (error) {
1132
+ cleanupError ??= error;
1133
+ }
1134
+ if (cleanupError) throw cleanupError;
1075
1135
  },
1076
1136
  };
1137
+ } catch (error) {
1138
+ try {
1139
+ await serverSetupCleanup?.();
1140
+ } catch {}
1141
+ try {
1142
+ await litsxPipeline?.dispose();
1143
+ } catch {}
1144
+ throw error;
1145
+ }
1077
1146
  }
1078
1147
 
1079
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
  *
@@ -0,0 +1,137 @@
1
+ import type {
2
+ TransformLitsxOptions,
3
+ TransformLitsxResult,
4
+ } from "@litsx/compiler";
5
+
6
+ export type EvolitMode = "development" | "production";
7
+ export type EvolitLitsxTarget = "server" | "client";
8
+
9
+ export type EvolitLitsxCompilerOptions = Omit<
10
+ TransformLitsxOptions,
11
+ "filename" | "ssr" | "reactCompat"
12
+ > & {
13
+ /** Evolit always uses native LitSX lowering. */
14
+ reactCompat?: false;
15
+ };
16
+
17
+ export type EvolitLitsxOutput = {
18
+ id: string;
19
+ kind: "asset" | "module" | "style";
20
+ content: string | Uint8Array;
21
+ specifier?: string;
22
+ document?: boolean;
23
+ };
24
+
25
+ export type EvolitLitsxContribution = Partial<TransformLitsxResult> & {
26
+ dependencies?: string[];
27
+ outputs?: EvolitLitsxOutput[];
28
+ };
29
+
30
+ export type EvolitLitsxIntegrationContext = {
31
+ readonly projectRoot: string;
32
+ readonly mode: EvolitMode;
33
+ readonly identity: Readonly<{ id: string }>;
34
+ };
35
+
36
+ export type EvolitLitsxModuleContext = {
37
+ readonly sourcePath: string;
38
+ readonly source: string;
39
+ readonly target: EvolitLitsxTarget;
40
+ readonly ssr: boolean;
41
+ readonly sourceMaps: boolean;
42
+ readonly generation: number;
43
+ readonly result: TransformLitsxResult;
44
+ };
45
+
46
+ export type EvolitLitsxIntegrationInstance = {
47
+ compiler?: EvolitLitsxCompilerOptions | ((context: {
48
+ filename: string;
49
+ ssr: boolean;
50
+ sourceMaps: boolean;
51
+ mode: EvolitMode;
52
+ }) => EvolitLitsxCompilerOptions);
53
+ resolveModule?(context: Readonly<{
54
+ specifier: string;
55
+ importer: string;
56
+ target: EvolitLitsxTarget;
57
+ ssr: boolean;
58
+ sourceMaps: boolean;
59
+ generation: number;
60
+ mode: EvolitMode;
61
+ }>): null | void | {
62
+ code: string;
63
+ map?: unknown;
64
+ dependencies?: string[];
65
+ } | Promise<null | void | {
66
+ code: string;
67
+ map?: unknown;
68
+ dependencies?: string[];
69
+ }>;
70
+ processModule?(context: EvolitLitsxModuleContext): EvolitLitsxContribution | void | Promise<EvolitLitsxContribution | void>;
71
+ finalize?(context: Readonly<Record<string, unknown> & {
72
+ generation: number;
73
+ mode: EvolitMode;
74
+ }>): EvolitLitsxContribution | void | Promise<EvolitLitsxContribution | void>;
75
+ invalidate?(context: Readonly<{
76
+ paths: string[] | null;
77
+ affected: boolean;
78
+ generation: number;
79
+ mode: EvolitMode;
80
+ }>): void | Promise<void>;
81
+ forget?(context: Readonly<{
82
+ moduleId: string;
83
+ generation: number;
84
+ mode: EvolitMode;
85
+ }>): void | Promise<void>;
86
+ dispose?(): void | Promise<void>;
87
+ };
88
+
89
+ export type EvolitLitsxIntegration = {
90
+ readonly name: string;
91
+ create(context: EvolitLitsxIntegrationContext): EvolitLitsxIntegrationInstance | Promise<EvolitLitsxIntegrationInstance>;
92
+ };
93
+
94
+ export type EvolitLitsxConfig = {
95
+ compiler?: EvolitLitsxCompilerOptions;
96
+ integrations?: EvolitLitsxIntegration[];
97
+ };
98
+
99
+ export type EvolitConfig = {
100
+ litsx?: EvolitLitsxConfig;
101
+ [key: string]: unknown;
102
+ };
103
+
104
+ export type EvolitLitsxPipeline = {
105
+ readonly identity: Readonly<{ id: string }>;
106
+ readonly generation: number;
107
+ readonly dependencies: string[];
108
+ getCompilerOptions(context: { filename: string; ssr: boolean; sourceMaps: boolean }): TransformLitsxOptions;
109
+ resolveModule(specifier: string, context: {
110
+ importer: string;
111
+ target: EvolitLitsxTarget;
112
+ ssr: boolean;
113
+ sourceMaps: boolean;
114
+ }): Promise<string | null>;
115
+ registerResolvedModule(specifier: string, sourcePath: string, outputPath: string): void;
116
+ processModule(result: TransformLitsxResult, context: Omit<EvolitLitsxModuleContext, "result" | "generation">): Promise<TransformLitsxResult & { evolitDependencies: string[] }>;
117
+ finalize(context?: Record<string, unknown>): Promise<null | {
118
+ assets: Array<{ integration: string; id: string; kind: string; outputPath: string; hash: string; size: number }>;
119
+ documentStyles: Array<{ integration: string; id: string; kind: string; outputPath: string; hash: string; size: number }>;
120
+ }>;
121
+ invalidate(changedPaths?: string[] | null): Promise<boolean>;
122
+ forget(moduleId: string): Promise<void>;
123
+ dispose(): Promise<void>;
124
+ };
125
+
126
+ export function createLitsxPipeline(options: {
127
+ projectRoot: string;
128
+ mode: EvolitMode;
129
+ config?: EvolitConfig;
130
+ }): Promise<EvolitLitsxPipeline | null>;
131
+
132
+ export function defineEvolitConfig<T extends EvolitConfig>(config: T): T;
133
+ export function defineLitsxIntegration<T extends EvolitLitsxIntegration>(integration: T): T;
134
+ export function mergeLitsxAssetsIntoManifest<T extends Record<string, unknown>>(
135
+ manifest: T,
136
+ integrationManifest: Awaited<ReturnType<EvolitLitsxPipeline["finalize"]>>,
137
+ ): T;