evolit 0.4.3 → 0.4.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/README.md +11 -0
- package/package.json +3 -2
- package/src/build.js +1 -1
- package/src/client-assets.js +28 -5
- package/src/compiler.js +21 -20
- package/src/deployment-runtime.js +1 -1
- package/src/litsx-pipeline.js +97 -9
- package/src/urql-ssr.js +26 -8
package/README.md
CHANGED
|
@@ -444,6 +444,17 @@ configuration therefore stays isolated per render, extracted SSR data is read
|
|
|
444
444
|
before the scope closes, and integration response headers are included before
|
|
445
445
|
the response is cached.
|
|
446
446
|
|
|
447
|
+
The supported SSR combination is `evolit@0.4.5` or newer,
|
|
448
|
+
`@litsx/core@1.0.0-next.8` or newer, and `@litsx/urql@0.4.2` or newer. Evolit
|
|
449
|
+
resolves the server-conditioned URQL adapter from the application project and
|
|
450
|
+
uses that same module instance for setup, request resources, route rendering,
|
|
451
|
+
SSR extraction, and cleanup.
|
|
452
|
+
|
|
453
|
+
The concurrent UnoCSS + URQL regression gate is validated with
|
|
454
|
+
`evolit@0.4.5`, `@litsx/compiler@1.0.0-next.13`,
|
|
455
|
+
`@litsx/ssr@1.0.0-next.5`, `@litsx/unocss@1.0.0-next.8`,
|
|
456
|
+
`@litsx/urql@0.4.2`, and `unocss@66.8.1`.
|
|
457
|
+
|
|
447
458
|
## Extensions
|
|
448
459
|
|
|
449
460
|
Optional integrations are configured explicitly in `evolit.config.js`. Core only coordinates their
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "evolit",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.5",
|
|
4
4
|
"description": "A convention-driven application framework for LitSX and web components.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "yarn@4.10.3",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"ws": "^8.18.3"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
|
-
"@litsx/urql": "^0.4.
|
|
78
|
+
"@litsx/urql": "^0.4.2"
|
|
79
79
|
},
|
|
80
80
|
"peerDependenciesMeta": {
|
|
81
81
|
"@litsx/urql": {
|
|
@@ -85,6 +85,7 @@
|
|
|
85
85
|
"devDependencies": {
|
|
86
86
|
"@litsx/tailwind": "1.0.0-next.8",
|
|
87
87
|
"@litsx/unocss": "1.0.0-next.8",
|
|
88
|
+
"@litsx/urql": "^0.4.2",
|
|
88
89
|
"@playwright/test": "^1.62.0",
|
|
89
90
|
"@webcomponents/scoped-custom-element-registry": "^0.0.10",
|
|
90
91
|
"unocss": "66.8.1",
|
package/src/build.js
CHANGED
package/src/client-assets.js
CHANGED
|
@@ -581,7 +581,22 @@ function createSharedChunkGroup(id) {
|
|
|
581
581
|
return "vendor-litsx";
|
|
582
582
|
}
|
|
583
583
|
|
|
584
|
-
return
|
|
584
|
+
return undefined;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
function createBrowserEnvironmentPlugin(mode) {
|
|
588
|
+
const nodeEnvironment = mode === "production" ? "production" : "development";
|
|
589
|
+
const replacement = JSON.stringify(nodeEnvironment);
|
|
590
|
+
return {
|
|
591
|
+
name: "evolit-browser-environment",
|
|
592
|
+
transform(code) {
|
|
593
|
+
if (!code.includes("process.env.NODE_ENV")) return null;
|
|
594
|
+
return {
|
|
595
|
+
code: code.replaceAll("process.env.NODE_ENV", replacement),
|
|
596
|
+
map: null,
|
|
597
|
+
};
|
|
598
|
+
},
|
|
599
|
+
};
|
|
585
600
|
}
|
|
586
601
|
|
|
587
602
|
function isClientAssetStubModule(relativePath) {
|
|
@@ -746,6 +761,7 @@ export async function buildSharedVendorRuntime(
|
|
|
746
761
|
.map((group) => buildSharedVendorRuntime(projectRoot, mode, {
|
|
747
762
|
...options,
|
|
748
763
|
additionalEntrySpecifiers: group.specifiers,
|
|
764
|
+
externalSpecifiers: developmentGroups.baseSpecifiers,
|
|
749
765
|
vendorGroup: group.vendorGroup,
|
|
750
766
|
includeBase: false,
|
|
751
767
|
})),
|
|
@@ -790,9 +806,16 @@ export async function buildSharedVendorRuntime(
|
|
|
790
806
|
const specifierByEntryName = new Map(
|
|
791
807
|
specifiers.map((specifier) => [createSharedEntryName(specifier), specifier]),
|
|
792
808
|
);
|
|
809
|
+
const externalSpecifiers = new Set(
|
|
810
|
+
Array.isArray(options.externalSpecifiers) ? options.externalSpecifiers : [],
|
|
811
|
+
);
|
|
793
812
|
const bundle = await rollup({
|
|
794
813
|
input: inputEntries,
|
|
814
|
+
external(source) {
|
|
815
|
+
return externalSpecifiers.has(source);
|
|
816
|
+
},
|
|
795
817
|
plugins: [
|
|
818
|
+
createBrowserEnvironmentPlugin(mode),
|
|
796
819
|
{
|
|
797
820
|
name: "evolit-browser-exports",
|
|
798
821
|
resolveId(source) {
|
|
@@ -814,10 +837,6 @@ export async function buildSharedVendorRuntime(
|
|
|
814
837
|
],
|
|
815
838
|
preserveEntrySignatures: "strict",
|
|
816
839
|
onwarn(warning, warn) {
|
|
817
|
-
if (warning.code === "CIRCULAR_DEPENDENCY") {
|
|
818
|
-
return;
|
|
819
|
-
}
|
|
820
|
-
|
|
821
840
|
warn(warning);
|
|
822
841
|
},
|
|
823
842
|
});
|
|
@@ -836,6 +855,10 @@ export async function buildSharedVendorRuntime(
|
|
|
836
855
|
manualChunks(id) {
|
|
837
856
|
return createSharedChunkGroup(id);
|
|
838
857
|
},
|
|
858
|
+
paths(id) {
|
|
859
|
+
if (!externalSpecifiers.has(id)) return id;
|
|
860
|
+
return `/_evolit/shared/base/${createSharedEntryName(id)}.mjs`;
|
|
861
|
+
},
|
|
839
862
|
});
|
|
840
863
|
|
|
841
864
|
const imports = {};
|
package/src/compiler.js
CHANGED
|
@@ -28,6 +28,7 @@ const RESOLVABLE_IMPORT_EXTENSIONS = [
|
|
|
28
28
|
const developmentGraphCache = new Map();
|
|
29
29
|
const developmentGraphDependencies = new Map();
|
|
30
30
|
const developmentModuleNamespaceCache = new Map();
|
|
31
|
+
const productionModuleNamespaceCache = new Map();
|
|
31
32
|
const developmentGraphVersions = new Map();
|
|
32
33
|
const developmentUnmanagedImportWarnings = new Set();
|
|
33
34
|
const projectPathAliasesCache = new Map();
|
|
@@ -1785,34 +1786,34 @@ export async function emitClientStaticAssets(assetPaths, options = {}) {
|
|
|
1785
1786
|
|
|
1786
1787
|
export async function importCompiledModule(entryPath, options = {}) {
|
|
1787
1788
|
const mode = options.mode ?? "development";
|
|
1788
|
-
const normalizedOptions =
|
|
1789
|
-
|
|
1790
|
-
: options
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1789
|
+
const normalizedOptions = {
|
|
1790
|
+
...options,
|
|
1791
|
+
projectRoot: path.resolve(options.projectRoot ?? process.cwd()),
|
|
1792
|
+
};
|
|
1793
|
+
const cacheKey = getDevelopmentGraphCacheKey(path.resolve(entryPath), normalizedOptions);
|
|
1794
|
+
const moduleCache = mode === "development"
|
|
1795
|
+
? developmentModuleNamespaceCache
|
|
1796
|
+
: productionModuleNamespaceCache;
|
|
1797
|
+
const cachedModule = moduleCache.get(cacheKey);
|
|
1795
1798
|
if (cachedModule) {
|
|
1796
1799
|
return cachedModule;
|
|
1797
1800
|
}
|
|
1798
1801
|
|
|
1799
|
-
const
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
developmentModuleNamespaceCache.set(cacheKey, pendingModule);
|
|
1809
|
-
}
|
|
1802
|
+
const pendingModule = (async () => {
|
|
1803
|
+
const { entrypoint } = await compileModuleGraph(entryPath, normalizedOptions);
|
|
1804
|
+
const moduleUrl = new URL(pathToFileURL(entrypoint).href);
|
|
1805
|
+
if (mode === "development") {
|
|
1806
|
+
moduleUrl.searchParams.set("t", String(developmentGraphVersions.get(cacheKey) ?? 0));
|
|
1807
|
+
}
|
|
1808
|
+
return import(moduleUrl.href);
|
|
1809
|
+
})();
|
|
1810
|
+
moduleCache.set(cacheKey, pendingModule);
|
|
1810
1811
|
|
|
1811
1812
|
try {
|
|
1812
1813
|
return await pendingModule;
|
|
1813
1814
|
} catch (error) {
|
|
1814
|
-
if (
|
|
1815
|
-
|
|
1815
|
+
if (moduleCache.get(cacheKey) === pendingModule) {
|
|
1816
|
+
moduleCache.delete(cacheKey);
|
|
1816
1817
|
}
|
|
1817
1818
|
throw error;
|
|
1818
1819
|
}
|
package/src/litsx-pipeline.js
CHANGED
|
@@ -206,8 +206,13 @@ export async function createLitsxPipeline({ projectRoot, mode, config } = {}) {
|
|
|
206
206
|
const instances = [];
|
|
207
207
|
const names = new Set();
|
|
208
208
|
let disposed = false;
|
|
209
|
+
let closing = false;
|
|
209
210
|
let generation = 0;
|
|
210
211
|
let finalizedGeneration = -1;
|
|
212
|
+
let finalizationFlight = null;
|
|
213
|
+
let invalidationFlight = null;
|
|
214
|
+
let queuedFinalization = null;
|
|
215
|
+
let disposalPromise = null;
|
|
211
216
|
const dependencies = new Set();
|
|
212
217
|
const processedModules = new Set();
|
|
213
218
|
const moduleDependencies = new Map();
|
|
@@ -288,7 +293,7 @@ export async function createLitsxPipeline({ projectRoot, mode, config } = {}) {
|
|
|
288
293
|
}
|
|
289
294
|
|
|
290
295
|
function assertActive() {
|
|
291
|
-
if (disposed) throw new Error("This LitSX pipeline has already been disposed.");
|
|
296
|
+
if (closing || disposed) throw new Error("This LitSX pipeline has already been disposed.");
|
|
292
297
|
}
|
|
293
298
|
|
|
294
299
|
function recordContribution(integrationName, moduleId, contribution) {
|
|
@@ -496,16 +501,43 @@ export async function createLitsxPipeline({ projectRoot, mode, config } = {}) {
|
|
|
496
501
|
evolitDependencies: [...(moduleDependencies.get(moduleId) ?? [])].sort(),
|
|
497
502
|
};
|
|
498
503
|
},
|
|
499
|
-
|
|
504
|
+
finalize(context = {}) {
|
|
500
505
|
assertActive();
|
|
501
|
-
if (
|
|
506
|
+
if (invalidationFlight) {
|
|
507
|
+
if (queuedFinalization) return queuedFinalization.promise;
|
|
508
|
+
const awaitedInvalidation = invalidationFlight.promise;
|
|
509
|
+
const promise = awaitedInvalidation.then(() => {
|
|
510
|
+
if (queuedFinalization?.promise === promise) queuedFinalization = null;
|
|
511
|
+
return pipeline.finalize(context);
|
|
512
|
+
});
|
|
513
|
+
queuedFinalization = { promise };
|
|
514
|
+
promise.then(
|
|
515
|
+
() => {
|
|
516
|
+
if (queuedFinalization?.promise === promise) queuedFinalization = null;
|
|
517
|
+
},
|
|
518
|
+
() => {
|
|
519
|
+
if (queuedFinalization?.promise === promise) queuedFinalization = null;
|
|
520
|
+
},
|
|
521
|
+
);
|
|
522
|
+
return promise;
|
|
523
|
+
}
|
|
524
|
+
if (finalizedGeneration === generation) return Promise.resolve(null);
|
|
525
|
+
if (finalizationFlight?.generation === generation) return finalizationFlight.promise;
|
|
526
|
+
if (finalizationFlight) {
|
|
527
|
+
return finalizationFlight.promise.then(
|
|
528
|
+
() => pipeline.finalize(context),
|
|
529
|
+
() => pipeline.finalize(context),
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
const targetGeneration = generation;
|
|
533
|
+
const publishGeneration = async () => {
|
|
502
534
|
if (moduleDependencies.delete("<graph>")) rebuildDependencies();
|
|
503
535
|
removeOwnedContributions("<graph>");
|
|
504
536
|
for (const { descriptor, instance } of instances) {
|
|
505
537
|
if (typeof instance.finalize !== "function") continue;
|
|
506
538
|
try {
|
|
507
539
|
const contribution = normalizeContribution(
|
|
508
|
-
await instance.finalize(Object.freeze({ ...context, generation, mode })),
|
|
540
|
+
await instance.finalize(Object.freeze({ ...context, generation: targetGeneration, mode })),
|
|
509
541
|
descriptor.name,
|
|
510
542
|
resolvedProjectRoot,
|
|
511
543
|
);
|
|
@@ -539,6 +571,7 @@ export async function createLitsxPipeline({ projectRoot, mode, config } = {}) {
|
|
|
539
571
|
hadPreviousRoot: false,
|
|
540
572
|
installed: false,
|
|
541
573
|
};
|
|
574
|
+
rootOperations.push(operation);
|
|
542
575
|
if (outputs.length > 0) {
|
|
543
576
|
operation.stagingRoot = `${root}.staging-${randomUUID()}`;
|
|
544
577
|
await ensureDirectory(operation.stagingRoot);
|
|
@@ -558,7 +591,6 @@ export async function createLitsxPipeline({ projectRoot, mode, config } = {}) {
|
|
|
558
591
|
if (output.kind === "style" && output.document) manifest.documentStyles.push(record);
|
|
559
592
|
}
|
|
560
593
|
}
|
|
561
|
-
rootOperations.push(operation);
|
|
562
594
|
}
|
|
563
595
|
|
|
564
596
|
for (const { output, integrationName } of pendingOutputs.values()) {
|
|
@@ -634,10 +666,22 @@ export async function createLitsxPipeline({ projectRoot, mode, config } = {}) {
|
|
|
634
666
|
if (operation.stagingRoot) publishedRoots.set(operation.integrationName, operation.root);
|
|
635
667
|
else publishedRoots.delete(operation.integrationName);
|
|
636
668
|
}
|
|
637
|
-
finalizedGeneration =
|
|
669
|
+
finalizedGeneration = targetGeneration;
|
|
638
670
|
return manifest;
|
|
671
|
+
};
|
|
672
|
+
const promise = publishGeneration();
|
|
673
|
+
finalizationFlight = { generation: targetGeneration, promise };
|
|
674
|
+
promise.then(
|
|
675
|
+
() => {
|
|
676
|
+
if (finalizationFlight?.promise === promise) finalizationFlight = null;
|
|
677
|
+
},
|
|
678
|
+
() => {
|
|
679
|
+
if (finalizationFlight?.promise === promise) finalizationFlight = null;
|
|
680
|
+
},
|
|
681
|
+
);
|
|
682
|
+
return promise;
|
|
639
683
|
},
|
|
640
|
-
|
|
684
|
+
invalidate(changedPaths = null) {
|
|
641
685
|
assertActive();
|
|
642
686
|
const paths = Array.isArray(changedPaths)
|
|
643
687
|
? changedPaths.map((entry) => canonicalDependency(resolvedProjectRoot, entry))
|
|
@@ -645,6 +689,26 @@ export async function createLitsxPipeline({ projectRoot, mode, config } = {}) {
|
|
|
645
689
|
const affected = paths == null || paths.some((entry) => (
|
|
646
690
|
dependencies.has(entry) || processedModules.has(entry)
|
|
647
691
|
));
|
|
692
|
+
const previousInvalidation = invalidationFlight?.promise ?? null;
|
|
693
|
+
const activeFinalization = finalizationFlight?.promise ?? null;
|
|
694
|
+
const promise = (async () => {
|
|
695
|
+
if (previousInvalidation) {
|
|
696
|
+
try {
|
|
697
|
+
await previousInvalidation;
|
|
698
|
+
} catch {
|
|
699
|
+
// A later invalidation is allowed to recover from an earlier failure.
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
if (activeFinalization) {
|
|
703
|
+
try {
|
|
704
|
+
await activeFinalization;
|
|
705
|
+
} catch {
|
|
706
|
+
// Failed finalization leaves the generation retryable before invalidation.
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
if (closing || disposed) {
|
|
710
|
+
throw new Error("This LitSX pipeline has already been disposed.");
|
|
711
|
+
}
|
|
648
712
|
for (const { descriptor, instance } of instances) {
|
|
649
713
|
if (typeof instance.invalidate !== "function") continue;
|
|
650
714
|
try {
|
|
@@ -661,6 +725,17 @@ export async function createLitsxPipeline({ projectRoot, mode, config } = {}) {
|
|
|
661
725
|
}
|
|
662
726
|
}
|
|
663
727
|
return affected;
|
|
728
|
+
})();
|
|
729
|
+
invalidationFlight = { promise };
|
|
730
|
+
promise.then(
|
|
731
|
+
() => {
|
|
732
|
+
if (invalidationFlight?.promise === promise) invalidationFlight = null;
|
|
733
|
+
},
|
|
734
|
+
() => {
|
|
735
|
+
if (invalidationFlight?.promise === promise) invalidationFlight = null;
|
|
736
|
+
},
|
|
737
|
+
);
|
|
738
|
+
return promise;
|
|
664
739
|
},
|
|
665
740
|
async forget(moduleId) {
|
|
666
741
|
assertActive();
|
|
@@ -683,8 +758,19 @@ export async function createLitsxPipeline({ projectRoot, mode, config } = {}) {
|
|
|
683
758
|
await removePath(virtualModule);
|
|
684
759
|
}
|
|
685
760
|
},
|
|
686
|
-
|
|
687
|
-
if (
|
|
761
|
+
dispose() {
|
|
762
|
+
if (disposalPromise) return disposalPromise;
|
|
763
|
+
if (disposed) return Promise.resolve();
|
|
764
|
+
closing = true;
|
|
765
|
+
disposalPromise = (async () => {
|
|
766
|
+
for (const flight of [queuedFinalization, invalidationFlight, finalizationFlight]) {
|
|
767
|
+
if (!flight) continue;
|
|
768
|
+
try {
|
|
769
|
+
await flight.promise;
|
|
770
|
+
} catch {
|
|
771
|
+
// The original caller owns a lifecycle error; disposal still completes.
|
|
772
|
+
}
|
|
773
|
+
}
|
|
688
774
|
disposed = true;
|
|
689
775
|
let firstError = null;
|
|
690
776
|
for (const { descriptor, instance } of [...instances].reverse()) {
|
|
@@ -696,6 +782,8 @@ export async function createLitsxPipeline({ projectRoot, mode, config } = {}) {
|
|
|
696
782
|
}
|
|
697
783
|
await removePath(virtualRoot);
|
|
698
784
|
if (firstError) throw firstError;
|
|
785
|
+
})();
|
|
786
|
+
return disposalPromise;
|
|
699
787
|
},
|
|
700
788
|
};
|
|
701
789
|
|
package/src/urql-ssr.js
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
|
|
5
|
+
const adapterPromisesByModuleUrl = new Map();
|
|
2
6
|
|
|
3
7
|
function isMissingUrqlAdapter(error) {
|
|
4
|
-
return error?.code === "ERR_MODULE_NOT_FOUND"
|
|
8
|
+
return (error?.code === "ERR_MODULE_NOT_FOUND" || error?.code === "MODULE_NOT_FOUND")
|
|
5
9
|
&& String(error.message).includes("@litsx/urql");
|
|
6
10
|
}
|
|
7
11
|
|
|
8
|
-
|
|
12
|
+
function resolveSsrUrqlAdapterUrl(projectRoot) {
|
|
9
13
|
try {
|
|
10
|
-
|
|
14
|
+
const requireFromProject = createRequire(path.join(path.resolve(projectRoot), "package.json"));
|
|
15
|
+
return pathToFileURL(requireFromProject.resolve("@litsx/urql")).href;
|
|
11
16
|
} catch (error) {
|
|
12
17
|
if (isMissingUrqlAdapter(error)) {
|
|
13
18
|
return null;
|
|
@@ -16,9 +21,22 @@ async function loadSsrUrqlAdapter() {
|
|
|
16
21
|
}
|
|
17
22
|
}
|
|
18
23
|
|
|
19
|
-
export async function getSsrUrqlAdapter() {
|
|
20
|
-
|
|
21
|
-
return
|
|
24
|
+
export async function getSsrUrqlAdapter(projectRoot = process.cwd()) {
|
|
25
|
+
const moduleUrl = resolveSsrUrqlAdapterUrl(projectRoot);
|
|
26
|
+
if (!moduleUrl) return null;
|
|
27
|
+
let adapterPromise = adapterPromisesByModuleUrl.get(moduleUrl);
|
|
28
|
+
if (!adapterPromise) {
|
|
29
|
+
adapterPromise = import(moduleUrl);
|
|
30
|
+
adapterPromisesByModuleUrl.set(moduleUrl, adapterPromise);
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
return await adapterPromise;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
if (adapterPromisesByModuleUrl.get(moduleUrl) === adapterPromise) {
|
|
36
|
+
adapterPromisesByModuleUrl.delete(moduleUrl);
|
|
37
|
+
}
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
22
40
|
}
|
|
23
41
|
|
|
24
42
|
/**
|
|
@@ -40,7 +58,7 @@ export async function runWithOptionalSsrUrqlScope(
|
|
|
40
58
|
}
|
|
41
59
|
const adapter = Object.hasOwn(options, "adapter")
|
|
42
60
|
? options.adapter
|
|
43
|
-
: await getSsrUrqlAdapter();
|
|
61
|
+
: await getSsrUrqlAdapter(options.projectRoot);
|
|
44
62
|
if (!adapter) {
|
|
45
63
|
return callback(null);
|
|
46
64
|
}
|