evolit 0.1.0-alpha.24 → 0.1.0-alpha.26
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 +27 -0
- package/package.json +8 -5
- package/src/build.js +152 -50
- package/src/client-assets.js +118 -20
- package/src/compiler.js +695 -11
- package/src/config.js +25 -0
- package/src/deployment-runtime.js +311 -79
- package/src/render.js +9 -1
- package/src/server.js +12 -10
- package/src/ssr-adapter.js +6 -0
package/README.md
CHANGED
|
@@ -317,6 +317,33 @@ export default {
|
|
|
317
317
|
};
|
|
318
318
|
```
|
|
319
319
|
|
|
320
|
+
For monorepos, development watches only the application project by default. Additional source
|
|
321
|
+
trees can be opted into explicitly; generated output and dependency directories below every root
|
|
322
|
+
remain ignored:
|
|
323
|
+
|
|
324
|
+
```js
|
|
325
|
+
export default {
|
|
326
|
+
development: {
|
|
327
|
+
managedSourceRoots: ["../packages/design-system/src"],
|
|
328
|
+
},
|
|
329
|
+
};
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Imports outside these roots remain unmanaged and produce the existing development warning.
|
|
333
|
+
|
|
334
|
+
Evolit normally discovers browser boundaries from the static module graph and reconciles that
|
|
335
|
+
inventory with the components actually emitted by SSR. A genuinely computed import cannot be
|
|
336
|
+
enumerated at build time, so production applications can declare only those exceptional modules:
|
|
337
|
+
|
|
338
|
+
```js
|
|
339
|
+
export default {
|
|
340
|
+
clientBoundaries: ["./src/components/dynamic-card.litsx", "@acme/ui/product-card"],
|
|
341
|
+
};
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
These modules are materialized as build artifacts, but are not automatically imported or
|
|
345
|
+
preloaded. They reach the browser only when an SSR hydration root references them.
|
|
346
|
+
|
|
320
347
|
The framework also exports `ObjectStorageResponseCacheStore`, which is intended for object-store
|
|
321
348
|
backends such as S3. A concrete app can wire it to the AWS SDK without pulling AWS dependencies
|
|
322
349
|
into `evolit` itself.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "evolit",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.26",
|
|
4
4
|
"description": "A convention-driven application framework for LitSX and web components.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "yarn@4.10.3",
|
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
"homepage": "https://github.com/litsxdev/nextsx#readme",
|
|
15
15
|
"files": [
|
|
16
16
|
"src",
|
|
17
|
-
"templates"
|
|
17
|
+
"templates/default/app/**",
|
|
18
|
+
"templates/default/jsconfig.json",
|
|
19
|
+
"templates/default/yarnrc.yml.template"
|
|
18
20
|
],
|
|
19
21
|
"bin": "./src/cli.js",
|
|
20
22
|
"main": "./src/index.js",
|
|
@@ -52,10 +54,11 @@
|
|
|
52
54
|
},
|
|
53
55
|
"dependencies": {
|
|
54
56
|
"@jridgewell/remapping": "^2.3.5",
|
|
55
|
-
"@litsx/compiler": "0.
|
|
56
|
-
"@litsx/core": "0.17.0-canary-feat-ssr-
|
|
57
|
-
"@litsx/ssr": "0.2.0-canary-feat-ssr-
|
|
57
|
+
"@litsx/compiler": "0.10.0-canary-feat-ssr-20260731112627",
|
|
58
|
+
"@litsx/core": "0.17.0-canary-feat-ssr-20260731112627",
|
|
59
|
+
"@litsx/ssr": "0.2.0-canary-feat-ssr-20260731112627",
|
|
58
60
|
"@litsx/typescript": "^0.9.0",
|
|
61
|
+
"@rollup/plugin-commonjs": "^29.0.0",
|
|
59
62
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
60
63
|
"lit": "^3.3.3",
|
|
61
64
|
"magic-string": "^1.1.0",
|
package/src/build.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
3
4
|
import {
|
|
4
5
|
discoverAppRouteHandlers,
|
|
5
6
|
discoverAppRoutes,
|
|
@@ -15,13 +16,20 @@ import {
|
|
|
15
16
|
emitBundledClientAssets,
|
|
16
17
|
emitHashedClientAssets,
|
|
17
18
|
normalizeHydrationDataForClient,
|
|
19
|
+
resolveServerStyleUrls,
|
|
18
20
|
resolveHydrationRootClientImports,
|
|
19
|
-
resolveRouteClientImports,
|
|
20
21
|
resolveSharedVendorModuleUrl,
|
|
21
22
|
rewriteHydrationDataScript,
|
|
22
23
|
rewriteServerAssetPlaceholders,
|
|
23
24
|
} from "./client-assets.js";
|
|
24
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
collectClientGraphInventory,
|
|
27
|
+
compileModuleGraph,
|
|
28
|
+
emitClientStaticAssets,
|
|
29
|
+
getClientStaticAssetModule,
|
|
30
|
+
importCompiledModule,
|
|
31
|
+
resolveProjectModuleSpecifier,
|
|
32
|
+
} from "./compiler.js";
|
|
25
33
|
import { loadEvolitConfig } from "./config.js";
|
|
26
34
|
import {
|
|
27
35
|
BUILD_DIRECTORY,
|
|
@@ -125,7 +133,37 @@ export async function buildProject(projectRoot) {
|
|
|
125
133
|
const routeHandlers = await discoverAppRouteHandlers(projectRoot);
|
|
126
134
|
const buildRoot = path.join(projectRoot, INTERNAL_DIRECTORY, BUILD_DIRECTORY);
|
|
127
135
|
const entryClientModules = new Set();
|
|
136
|
+
const serverAssetImportsByEntry = {};
|
|
137
|
+
const clientBoundariesByEntry = {};
|
|
128
138
|
const deployHandlers = [];
|
|
139
|
+
const compiledClientBoundaries = new Map();
|
|
140
|
+
const inventoriesBySourceEntry = new Map();
|
|
141
|
+
|
|
142
|
+
function getEntryInventory(entryPath) {
|
|
143
|
+
let inventory = inventoriesBySourceEntry.get(entryPath);
|
|
144
|
+
if (!inventory) {
|
|
145
|
+
inventory = collectClientGraphInventory([entryPath], { projectRoot });
|
|
146
|
+
inventoriesBySourceEntry.set(entryPath, inventory);
|
|
147
|
+
}
|
|
148
|
+
return inventory;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function compileProductionClientBoundary(sourcePath) {
|
|
152
|
+
let clientModule = compiledClientBoundaries.get(sourcePath);
|
|
153
|
+
if (clientModule) return clientModule;
|
|
154
|
+
const clientBuild = await compileModuleGraph(sourcePath, {
|
|
155
|
+
projectRoot,
|
|
156
|
+
mode: "production",
|
|
157
|
+
sourceMaps: true,
|
|
158
|
+
target: "client",
|
|
159
|
+
});
|
|
160
|
+
clientModule = path.relative(clientBuild.outputRoot, clientBuild.entrypoint)
|
|
161
|
+
.split(path.sep)
|
|
162
|
+
.join("/");
|
|
163
|
+
compiledClientBoundaries.set(sourcePath, clientModule);
|
|
164
|
+
entryClientModules.add(clientModule);
|
|
165
|
+
return clientModule;
|
|
166
|
+
}
|
|
129
167
|
|
|
130
168
|
await ensureDirectory(buildRoot);
|
|
131
169
|
|
|
@@ -168,16 +206,6 @@ export async function buildProject(projectRoot) {
|
|
|
168
206
|
target: "server",
|
|
169
207
|
});
|
|
170
208
|
|
|
171
|
-
const pageClientBuild = await compileModuleGraph(route.page, {
|
|
172
|
-
projectRoot,
|
|
173
|
-
mode: "production",
|
|
174
|
-
sourceMaps: true,
|
|
175
|
-
target: "client",
|
|
176
|
-
});
|
|
177
|
-
entryClientModules.add(
|
|
178
|
-
path.relative(pageClientBuild.outputRoot, pageClientBuild.entrypoint).split(path.sep).join("/"),
|
|
179
|
-
);
|
|
180
|
-
|
|
181
209
|
for (const layoutPath of route.layouts) {
|
|
182
210
|
await compileModuleGraph(layoutPath, {
|
|
183
211
|
projectRoot,
|
|
@@ -187,15 +215,6 @@ export async function buildProject(projectRoot) {
|
|
|
187
215
|
target: "server",
|
|
188
216
|
});
|
|
189
217
|
|
|
190
|
-
const layoutClientBuild = await compileModuleGraph(layoutPath, {
|
|
191
|
-
projectRoot,
|
|
192
|
-
mode: "production",
|
|
193
|
-
sourceMaps: true,
|
|
194
|
-
target: "client",
|
|
195
|
-
});
|
|
196
|
-
entryClientModules.add(
|
|
197
|
-
path.relative(layoutClientBuild.outputRoot, layoutClientBuild.entrypoint).split(path.sep).join("/"),
|
|
198
|
-
);
|
|
199
218
|
}
|
|
200
219
|
|
|
201
220
|
const boundaryModules = [
|
|
@@ -210,23 +229,70 @@ export async function buildProject(projectRoot) {
|
|
|
210
229
|
ssr: true,
|
|
211
230
|
target: "server",
|
|
212
231
|
});
|
|
232
|
+
}
|
|
213
233
|
|
|
214
|
-
|
|
234
|
+
const toProjectRelative = (filePath) => path.relative(projectRoot, filePath).split(path.sep).join("/");
|
|
235
|
+
const segmentEntries = [...new Set([route.page, ...route.layouts, ...boundaryModules])];
|
|
236
|
+
const inventoriesByEntry = new Map(await Promise.all(segmentEntries.map(async (entryPath) => [
|
|
237
|
+
entryPath,
|
|
238
|
+
await getEntryInventory(entryPath),
|
|
239
|
+
])));
|
|
240
|
+
const allStyles = new Set();
|
|
241
|
+
const allAssets = new Set();
|
|
242
|
+
const allClientBoundaries = new Set();
|
|
243
|
+
for (const [entryPath, entryInventory] of inventoriesByEntry) {
|
|
244
|
+
serverAssetImportsByEntry[toProjectRelative(entryPath)] = {
|
|
245
|
+
styles: entryInventory.styles.map((filePath) => getClientStaticAssetModule(projectRoot, filePath)),
|
|
246
|
+
assets: entryInventory.assets.map((filePath) => getClientStaticAssetModule(projectRoot, filePath)),
|
|
247
|
+
};
|
|
248
|
+
entryInventory.styles.forEach((filePath) => allStyles.add(filePath));
|
|
249
|
+
entryInventory.assets.forEach((filePath) => allAssets.add(filePath));
|
|
250
|
+
entryInventory.clientBoundaries.forEach((filePath) => allClientBoundaries.add(filePath));
|
|
251
|
+
}
|
|
252
|
+
await emitClientStaticAssets([...allStyles, ...allAssets], {
|
|
253
|
+
projectRoot,
|
|
254
|
+
mode: "production",
|
|
255
|
+
});
|
|
256
|
+
const compiledBoundaryModules = new Map();
|
|
257
|
+
for (const clientBoundary of allClientBoundaries) {
|
|
258
|
+
compiledBoundaryModules.set(
|
|
259
|
+
clientBoundary,
|
|
260
|
+
await compileProductionClientBoundary(clientBoundary),
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
for (const [entryPath, entryInventory] of inventoriesByEntry) {
|
|
264
|
+
clientBoundariesByEntry[toProjectRelative(entryPath)] = entryInventory.clientBoundaries
|
|
265
|
+
.map((clientBoundary) => compiledBoundaryModules.get(clientBoundary))
|
|
266
|
+
.filter(Boolean)
|
|
267
|
+
.sort();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const configuredClientBoundaries = evolitConfig.clientBoundaries ?? [];
|
|
272
|
+
if (!Array.isArray(configuredClientBoundaries)) {
|
|
273
|
+
throw new Error("Expected clientBoundaries in evolit.config.js to be an array of module specifiers.");
|
|
274
|
+
}
|
|
275
|
+
for (const specifier of configuredClientBoundaries) {
|
|
276
|
+
if (typeof specifier !== "string" || specifier.length === 0) {
|
|
277
|
+
throw new Error("Expected every clientBoundaries entry to be a non-empty module specifier.");
|
|
278
|
+
}
|
|
279
|
+
const sourcePath = path.isAbsolute(specifier)
|
|
280
|
+
? specifier
|
|
281
|
+
: await resolveProjectModuleSpecifier(
|
|
215
282
|
projectRoot,
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
target: "client",
|
|
219
|
-
});
|
|
220
|
-
entryClientModules.add(
|
|
221
|
-
path.relative(boundaryClientBuild.outputRoot, boundaryClientBuild.entrypoint)
|
|
222
|
-
.split(path.sep)
|
|
223
|
-
.join("/"),
|
|
283
|
+
path.join(projectRoot, "evolit.config.js"),
|
|
284
|
+
specifier,
|
|
224
285
|
);
|
|
286
|
+
if (!sourcePath) {
|
|
287
|
+
throw new Error(`Unable to resolve configured client boundary ${JSON.stringify(specifier)}.`);
|
|
225
288
|
}
|
|
289
|
+
await compileProductionClientBoundary(sourcePath);
|
|
226
290
|
}
|
|
227
291
|
|
|
228
292
|
const clientAssets = await emitBundledClientAssets(projectRoot, {
|
|
229
293
|
entryClientModules,
|
|
294
|
+
serverAssetImportsByEntry,
|
|
295
|
+
clientBoundariesByEntry,
|
|
230
296
|
});
|
|
231
297
|
const staticAssetPublicUrls = createStaticAssetPublicUrlMap(clientAssets);
|
|
232
298
|
await rewriteServerAssetPlaceholders(projectRoot, clientAssets);
|
|
@@ -244,19 +310,68 @@ export async function buildProject(projectRoot) {
|
|
|
244
310
|
);
|
|
245
311
|
const ssrAdapter = createSsrAdapter({
|
|
246
312
|
assetResolver,
|
|
313
|
+
async onSsrResult({ routeResult, result }) {
|
|
314
|
+
const renderedModules = [...new Set([
|
|
315
|
+
...(Array.isArray(result.clientImports) ? result.clientImports : []),
|
|
316
|
+
...(Array.isArray(result.hydrationData?.roots)
|
|
317
|
+
? result.hydrationData.roots.map((root) => root?.moduleId)
|
|
318
|
+
: []),
|
|
319
|
+
].filter((moduleId) => typeof moduleId === "string" && moduleId.length > 0))];
|
|
320
|
+
const unresolved = [];
|
|
321
|
+
for (const moduleId of renderedModules) {
|
|
322
|
+
if (assetResolver(moduleId) || clientAssets.byPublicPath?.[moduleId]) continue;
|
|
323
|
+
const importerPath = routeResult.boundaryModule
|
|
324
|
+
?? routeResult.route?.page
|
|
325
|
+
?? path.join(projectRoot, "app", "page.litsx");
|
|
326
|
+
let sourcePath = null;
|
|
327
|
+
if (moduleId.startsWith("file:")) {
|
|
328
|
+
try { sourcePath = fileURLToPath(moduleId); } catch {}
|
|
329
|
+
} else if (path.isAbsolute(moduleId) && !moduleId.startsWith("/app/") && !moduleId.startsWith("/src/")) {
|
|
330
|
+
sourcePath = moduleId;
|
|
331
|
+
} else if (moduleId.startsWith("/")) {
|
|
332
|
+
sourcePath = await resolveProjectModuleSpecifier(projectRoot, importerPath, `.${moduleId}`);
|
|
333
|
+
} else {
|
|
334
|
+
sourcePath = await resolveProjectModuleSpecifier(projectRoot, importerPath, moduleId);
|
|
335
|
+
}
|
|
336
|
+
const publicUrl = sourcePath ? assetResolver(sourcePath) : null;
|
|
337
|
+
if (!publicUrl) {
|
|
338
|
+
unresolved.push(moduleId);
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
for (const root of result.hydrationData?.roots ?? []) {
|
|
342
|
+
if (root?.moduleId === moduleId) root.moduleId = sourcePath;
|
|
343
|
+
}
|
|
344
|
+
if (Array.isArray(result.clientImports)) {
|
|
345
|
+
result.clientImports = result.clientImports.map((value) => value === moduleId ? publicUrl : value);
|
|
346
|
+
}
|
|
347
|
+
if (Array.isArray(result.hydrationData?.clientImports)) {
|
|
348
|
+
result.hydrationData.clientImports = result.hydrationData.clientImports
|
|
349
|
+
.map((value) => value === moduleId ? publicUrl : value);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
if (unresolved.length > 0) {
|
|
353
|
+
throw new Error(
|
|
354
|
+
`SSR rendered client boundaries without build artifacts for ${routeResult.route?.pathname ?? "route"}: `
|
|
355
|
+
+ `${unresolved.join(", ")}.`,
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
},
|
|
247
359
|
async resolveAdditionalHead({ routeResult, result }) {
|
|
248
|
-
const clientImports = [
|
|
360
|
+
const clientImports = [...new Set([
|
|
249
361
|
...(Array.isArray(result.clientImports) ? result.clientImports : []),
|
|
250
|
-
...
|
|
251
|
-
];
|
|
252
|
-
const hydratedClientImports =
|
|
362
|
+
...resolveHydrationRootClientImports(result.hydrationData, assetResolver),
|
|
363
|
+
])];
|
|
364
|
+
const hydratedClientImports = clientImports;
|
|
253
365
|
const urls = hydratedClientImports.length > 0
|
|
254
366
|
? [...new Set([
|
|
255
367
|
...hydratedClientImports,
|
|
256
368
|
...collectTransitiveAssetPreloads(hydratedClientImports, clientAssets),
|
|
257
369
|
])]
|
|
258
370
|
: [];
|
|
259
|
-
const styleUrls =
|
|
371
|
+
const styleUrls = [...new Set([
|
|
372
|
+
...collectTransitiveStyleUrls(clientImports, clientAssets),
|
|
373
|
+
...resolveServerStyleUrls(routeResult, projectRoot, clientAssets),
|
|
374
|
+
])];
|
|
260
375
|
|
|
261
376
|
return [
|
|
262
377
|
...urls.map((href) => `<link rel="modulepreload" href="${href}" data-evolit-route-asset="preload">`),
|
|
@@ -264,36 +379,23 @@ export async function buildProject(projectRoot) {
|
|
|
264
379
|
].join("\n");
|
|
265
380
|
},
|
|
266
381
|
resolveBootstrap({ routeResult, result }) {
|
|
267
|
-
const routeClientImports = resolveRouteClientImports(
|
|
268
|
-
routeResult,
|
|
269
|
-
projectRoot,
|
|
270
|
-
clientAssets,
|
|
271
|
-
);
|
|
272
382
|
return createHydrationBootstrap({
|
|
273
383
|
hydrationData: normalizeHydrationDataForClient(
|
|
274
384
|
result.hydrationData,
|
|
275
385
|
projectRoot,
|
|
276
|
-
|
|
386
|
+
resolveHydrationRootClientImports(result.hydrationData, assetResolver),
|
|
277
387
|
),
|
|
278
388
|
assetResolver,
|
|
279
389
|
hydrationModuleUrl,
|
|
280
390
|
});
|
|
281
391
|
},
|
|
282
392
|
transformDocument({ routeResult, result, document }) {
|
|
283
|
-
const routeClientImports = resolveRouteClientImports(
|
|
284
|
-
routeResult,
|
|
285
|
-
projectRoot,
|
|
286
|
-
clientAssets,
|
|
287
|
-
);
|
|
288
393
|
return rewriteHydrationDataScript(
|
|
289
394
|
document,
|
|
290
395
|
normalizeHydrationDataForClient(
|
|
291
396
|
result.hydrationData,
|
|
292
397
|
projectRoot,
|
|
293
|
-
|
|
294
|
-
...routeClientImports,
|
|
295
|
-
...resolveHydrationRootClientImports(result.hydrationData, assetResolver),
|
|
296
|
-
],
|
|
398
|
+
resolveHydrationRootClientImports(result.hydrationData, assetResolver),
|
|
297
399
|
),
|
|
298
400
|
);
|
|
299
401
|
},
|
package/src/client-assets.js
CHANGED
|
@@ -6,6 +6,7 @@ import { createRequire } from "node:module";
|
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import MagicString from "magic-string";
|
|
8
8
|
import { rollup } from "rollup";
|
|
9
|
+
import commonjs from "@rollup/plugin-commonjs";
|
|
9
10
|
import { nodeResolve } from "@rollup/plugin-node-resolve";
|
|
10
11
|
import {
|
|
11
12
|
BUILD_DIRECTORY,
|
|
@@ -54,6 +55,12 @@ export function resetDevelopmentAssetCaches(projectRoot) {
|
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
}
|
|
58
|
+
const resolutionPrefix = `${normalizedProjectRoot}::`;
|
|
59
|
+
for (const cache of [browserSpecifierFilePathCache, packageRootCache]) {
|
|
60
|
+
for (const key of cache.keys()) {
|
|
61
|
+
if (key.startsWith(resolutionPrefix)) cache.delete(key);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
57
64
|
}
|
|
58
65
|
|
|
59
66
|
function isBareSpecifier(specifier) {
|
|
@@ -125,13 +132,20 @@ function parsePackageSpecifier(specifier) {
|
|
|
125
132
|
};
|
|
126
133
|
}
|
|
127
134
|
|
|
128
|
-
export async function resolvePackageRoot(packageName) {
|
|
129
|
-
|
|
130
|
-
|
|
135
|
+
export async function resolvePackageRoot(packageName, options = {}) {
|
|
136
|
+
const resolveFrom = path.resolve(options.projectRoot ?? path.dirname(fileURLToPath(import.meta.url)));
|
|
137
|
+
const cacheKey = `${resolveFrom}::${packageName}`;
|
|
138
|
+
if (packageRootCache.has(cacheKey)) {
|
|
139
|
+
return packageRootCache.get(cacheKey);
|
|
131
140
|
}
|
|
132
141
|
|
|
133
142
|
const pendingResolution = (async () => {
|
|
134
|
-
|
|
143
|
+
let packageEntryPath;
|
|
144
|
+
try {
|
|
145
|
+
packageEntryPath = createRequire(path.join(resolveFrom, "package.json")).resolve(packageName);
|
|
146
|
+
} catch {
|
|
147
|
+
packageEntryPath = requireFromHere.resolve(packageName);
|
|
148
|
+
}
|
|
135
149
|
let currentPath = path.dirname(packageEntryPath);
|
|
136
150
|
|
|
137
151
|
while (true) {
|
|
@@ -157,12 +171,12 @@ export async function resolvePackageRoot(packageName) {
|
|
|
157
171
|
throw new Error(`Unable to resolve package root for ${packageName}`);
|
|
158
172
|
})();
|
|
159
173
|
|
|
160
|
-
packageRootCache.set(
|
|
174
|
+
packageRootCache.set(cacheKey, pendingResolution);
|
|
161
175
|
|
|
162
176
|
try {
|
|
163
177
|
return await pendingResolution;
|
|
164
178
|
} catch (error) {
|
|
165
|
-
packageRootCache.delete(
|
|
179
|
+
packageRootCache.delete(cacheKey);
|
|
166
180
|
throw error;
|
|
167
181
|
}
|
|
168
182
|
}
|
|
@@ -184,13 +198,15 @@ function pickBrowserExportTarget(target) {
|
|
|
184
198
|
?? null;
|
|
185
199
|
}
|
|
186
200
|
|
|
187
|
-
export async function resolveBrowserSpecifierFilePath(specifier) {
|
|
201
|
+
export async function resolveBrowserSpecifierFilePath(specifier, options = {}) {
|
|
188
202
|
if (!isBareSpecifier(specifier)) {
|
|
189
203
|
return null;
|
|
190
204
|
}
|
|
191
205
|
|
|
192
|
-
|
|
193
|
-
|
|
206
|
+
const resolveFrom = path.resolve(options.projectRoot ?? path.dirname(fileURLToPath(import.meta.url)));
|
|
207
|
+
const cacheKey = `${resolveFrom}::${specifier}`;
|
|
208
|
+
if (browserSpecifierFilePathCache.has(cacheKey)) {
|
|
209
|
+
return browserSpecifierFilePathCache.get(cacheKey);
|
|
194
210
|
}
|
|
195
211
|
|
|
196
212
|
const pendingResolution = (async () => {
|
|
@@ -200,13 +216,32 @@ export async function resolveBrowserSpecifierFilePath(specifier) {
|
|
|
200
216
|
}
|
|
201
217
|
|
|
202
218
|
const { packageName, subpath } = parsedSpecifier;
|
|
203
|
-
const { packageRoot, packageJson } = await resolvePackageRoot(packageName);
|
|
219
|
+
const { packageRoot, packageJson } = await resolvePackageRoot(packageName, { projectRoot: resolveFrom });
|
|
204
220
|
const exportKey = subpath.length > 0 ? `./${subpath}` : ".";
|
|
205
|
-
const
|
|
221
|
+
const exportsField = packageJson.exports;
|
|
222
|
+
const exportTarget = pickBrowserExportTarget(
|
|
223
|
+
exportsField && typeof exportsField === "object" && Object.keys(exportsField).some((key) => key.startsWith("."))
|
|
224
|
+
? exportsField[exportKey]
|
|
225
|
+
: exportKey === "." ? exportsField : null,
|
|
226
|
+
);
|
|
206
227
|
const fallbackTarget = subpath.length > 0
|
|
207
228
|
? `./${subpath}`
|
|
208
|
-
:
|
|
209
|
-
|
|
229
|
+
: typeof packageJson.browser === "string"
|
|
230
|
+
? packageJson.browser
|
|
231
|
+
: packageJson.module ?? packageJson.main ?? null;
|
|
232
|
+
let resolvedTarget = exportTarget ?? fallbackTarget;
|
|
233
|
+
if (resolvedTarget && packageJson.browser && typeof packageJson.browser === "object") {
|
|
234
|
+
const normalizedTarget = resolvedTarget.startsWith("./")
|
|
235
|
+
? resolvedTarget
|
|
236
|
+
: `./${resolvedTarget}`;
|
|
237
|
+
const browserTarget = packageJson.browser[normalizedTarget]
|
|
238
|
+
?? packageJson.browser[resolvedTarget]
|
|
239
|
+
?? packageJson.browser[exportKey];
|
|
240
|
+
if (browserTarget === false) {
|
|
241
|
+
throw new Error(`Browser entry is disabled for ${specifier}`);
|
|
242
|
+
}
|
|
243
|
+
if (typeof browserTarget === "string") resolvedTarget = browserTarget;
|
|
244
|
+
}
|
|
210
245
|
|
|
211
246
|
if (typeof resolvedTarget !== "string" || resolvedTarget.length === 0) {
|
|
212
247
|
throw new Error(`Unable to resolve browser entry for ${specifier}`);
|
|
@@ -215,12 +250,12 @@ export async function resolveBrowserSpecifierFilePath(specifier) {
|
|
|
215
250
|
return path.resolve(packageRoot, resolvedTarget);
|
|
216
251
|
})();
|
|
217
252
|
|
|
218
|
-
browserSpecifierFilePathCache.set(
|
|
253
|
+
browserSpecifierFilePathCache.set(cacheKey, pendingResolution);
|
|
219
254
|
|
|
220
255
|
try {
|
|
221
256
|
return await pendingResolution;
|
|
222
257
|
} catch (error) {
|
|
223
|
-
browserSpecifierFilePathCache.delete(
|
|
258
|
+
browserSpecifierFilePathCache.delete(cacheKey);
|
|
224
259
|
throw error;
|
|
225
260
|
}
|
|
226
261
|
}
|
|
@@ -272,16 +307,22 @@ export async function collectSharedVendorSpecifiers(
|
|
|
272
307
|
])].sort();
|
|
273
308
|
}
|
|
274
309
|
|
|
275
|
-
async function
|
|
310
|
+
async function createSharedRuntimeInputEntries(projectRoot, entriesRoot, specifiers) {
|
|
276
311
|
await fs.rm(entriesRoot, { recursive: true, force: true });
|
|
277
312
|
await ensureDirectory(entriesRoot);
|
|
278
313
|
|
|
279
314
|
const inputEntries = {};
|
|
280
315
|
for (const specifier of specifiers) {
|
|
281
316
|
const entryName = createSharedEntryName(specifier);
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
317
|
+
if (specifier === "@litsx/ssr/hydration") {
|
|
318
|
+
const entryPath = path.join(entriesRoot, `${entryName}.mjs`);
|
|
319
|
+
await fs.writeFile(entryPath, createSharedEntrySource(specifier), "utf8");
|
|
320
|
+
inputEntries[entryName] = entryPath;
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
inputEntries[entryName] = specifier === "evolit/navigation"
|
|
324
|
+
? EVOLIT_NAVIGATION_BROWSER_ENTRY
|
|
325
|
+
: await resolveBrowserSpecifierFilePath(specifier, { projectRoot });
|
|
285
326
|
}
|
|
286
327
|
|
|
287
328
|
return inputEntries;
|
|
@@ -508,7 +549,7 @@ export async function buildSharedVendorRuntime(
|
|
|
508
549
|
const specifiers = await collectSharedVendorSpecifiers(additionalEntrySpecifiers, {
|
|
509
550
|
includeBase: options.includeBase !== false,
|
|
510
551
|
});
|
|
511
|
-
const inputEntries = await
|
|
552
|
+
const inputEntries = await createSharedRuntimeInputEntries(projectRoot, entriesRoot, specifiers);
|
|
512
553
|
|
|
513
554
|
await fs.rm(sharedRoot, { recursive: true, force: true });
|
|
514
555
|
await ensureDirectory(sharedRoot);
|
|
@@ -534,7 +575,11 @@ export async function buildSharedVendorRuntime(
|
|
|
534
575
|
extensions: [".mjs", ".js", ".json"],
|
|
535
576
|
preferBuiltins: false,
|
|
536
577
|
}),
|
|
578
|
+
commonjs({
|
|
579
|
+
transformMixedEsModules: true,
|
|
580
|
+
}),
|
|
537
581
|
],
|
|
582
|
+
preserveEntrySignatures: "strict",
|
|
538
583
|
onwarn(warning, warn) {
|
|
539
584
|
if (warning.code === "CIRCULAR_DEPENDENCY") {
|
|
540
585
|
return;
|
|
@@ -1597,6 +1642,8 @@ export async function emitHashedClientAssets(projectRoot, options = {}) {
|
|
|
1597
1642
|
chunks,
|
|
1598
1643
|
styles,
|
|
1599
1644
|
resources,
|
|
1645
|
+
serverAssetImportsByEntry: options.serverAssetImportsByEntry ?? {},
|
|
1646
|
+
clientBoundariesByEntry: options.clientBoundariesByEntry ?? {},
|
|
1600
1647
|
assets: assets.sort((left, right) => left.clientModule.localeCompare(right.clientModule)),
|
|
1601
1648
|
};
|
|
1602
1649
|
|
|
@@ -1893,6 +1940,13 @@ async function bundleClientAssets(projectRoot, options = {}) {
|
|
|
1893
1940
|
[...scriptAssetRecords, ...nonScriptAssets, ...mapAssets]
|
|
1894
1941
|
.map((asset) => asset.outputPath),
|
|
1895
1942
|
);
|
|
1943
|
+
// Requests for an asset from the previous manifest can still be in flight
|
|
1944
|
+
// while a development rebuild publishes this one. Keep precisely that
|
|
1945
|
+
// generation for one rebuild so those requests do not resolve to a file
|
|
1946
|
+
// which this cleanup has just removed.
|
|
1947
|
+
for (const outputPath of options.retainOutputPaths ?? []) {
|
|
1948
|
+
retainedOutputPaths.add(outputPath);
|
|
1949
|
+
}
|
|
1896
1950
|
for (const filePath of await walkFiles(staticRoot)) {
|
|
1897
1951
|
if (!retainedOutputPaths.has(filePath)) {
|
|
1898
1952
|
await fs.rm(filePath, { force: true });
|
|
@@ -1914,6 +1968,8 @@ async function bundleClientAssets(projectRoot, options = {}) {
|
|
|
1914
1968
|
.filter((asset) => asset.kind === "asset" && !asset.clientModule.endsWith(".map"))
|
|
1915
1969
|
.map((asset) => asset.clientModule)
|
|
1916
1970
|
.sort(),
|
|
1971
|
+
serverAssetImportsByEntry: options.serverAssetImportsByEntry ?? {},
|
|
1972
|
+
clientBoundariesByEntry: options.clientBoundariesByEntry ?? {},
|
|
1917
1973
|
assets,
|
|
1918
1974
|
};
|
|
1919
1975
|
|
|
@@ -2092,6 +2148,40 @@ export function collectTransitiveStyleUrls(publicUrls, assetManifest) {
|
|
|
2092
2148
|
return [...styles].sort();
|
|
2093
2149
|
}
|
|
2094
2150
|
|
|
2151
|
+
function getServerAssetImports(routeResult, projectRoot, assetManifest) {
|
|
2152
|
+
if (routeResult?.serverAssetImports && typeof routeResult.serverAssetImports === "object") {
|
|
2153
|
+
return routeResult.serverAssetImports;
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
const entryPaths = routeResult?.boundaryModule
|
|
2157
|
+
? [routeResult.boundaryModule]
|
|
2158
|
+
: [
|
|
2159
|
+
...(routeResult?.route?.layouts ?? []),
|
|
2160
|
+
routeResult?.route?.page,
|
|
2161
|
+
].filter((entryPath) => typeof entryPath === "string");
|
|
2162
|
+
if (entryPaths.length === 0 || !assetManifest) {
|
|
2163
|
+
return { styles: [], assets: [] };
|
|
2164
|
+
}
|
|
2165
|
+
const styles = new Set();
|
|
2166
|
+
const assets = new Set();
|
|
2167
|
+
for (const entryPath of entryPaths) {
|
|
2168
|
+
const entry = path.relative(projectRoot, entryPath).split(path.sep).join("/");
|
|
2169
|
+
const imports = assetManifest.serverAssetImportsByEntry?.[entry];
|
|
2170
|
+
for (const style of imports?.styles ?? []) styles.add(style);
|
|
2171
|
+
for (const asset of imports?.assets ?? []) assets.add(asset);
|
|
2172
|
+
}
|
|
2173
|
+
return { styles: [...styles], assets: [...assets] };
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
export function resolveServerStyleUrls(routeResult, projectRoot, assetManifest) {
|
|
2177
|
+
const normalizedManifest = normalizeClientAssetManifest(assetManifest);
|
|
2178
|
+
if (!normalizedManifest) return [];
|
|
2179
|
+
const imports = getServerAssetImports(routeResult, projectRoot, normalizedManifest);
|
|
2180
|
+
return [...new Set((imports.styles ?? [])
|
|
2181
|
+
.map((style) => normalizedManifest.byClientModule[style])
|
|
2182
|
+
.filter((url) => typeof url === "string"))].sort();
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2095
2185
|
export function normalizeClientAssetManifest(manifest) {
|
|
2096
2186
|
if (!manifest || typeof manifest !== "object" || !Array.isArray(manifest.assets)) {
|
|
2097
2187
|
return null;
|
|
@@ -2106,6 +2196,14 @@ export function normalizeClientAssetManifest(manifest) {
|
|
|
2106
2196
|
chunks: Array.isArray(manifest.chunks) ? manifest.chunks : [],
|
|
2107
2197
|
styles: Array.isArray(manifest.styles) ? manifest.styles : [],
|
|
2108
2198
|
resources: Array.isArray(manifest.resources) ? manifest.resources : [],
|
|
2199
|
+
serverAssetImportsByEntry:
|
|
2200
|
+
manifest.serverAssetImportsByEntry && typeof manifest.serverAssetImportsByEntry === "object"
|
|
2201
|
+
? manifest.serverAssetImportsByEntry
|
|
2202
|
+
: {},
|
|
2203
|
+
clientBoundariesByEntry:
|
|
2204
|
+
manifest.clientBoundariesByEntry && typeof manifest.clientBoundariesByEntry === "object"
|
|
2205
|
+
? manifest.clientBoundariesByEntry
|
|
2206
|
+
: {},
|
|
2109
2207
|
assets: manifest.assets,
|
|
2110
2208
|
};
|
|
2111
2209
|
}
|