evolit 0.1.3 → 0.1.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 CHANGED
@@ -46,6 +46,31 @@ Generated applications declare `"@/*": ["./*"]` in `jsconfig.json`, so editors a
46
46
  use the same convention. An explicit `@/*` mapping in `jsconfig.json` or `tsconfig.json` takes
47
47
  priority when an application needs a different source root.
48
48
 
49
+ ### Package CSS and static assets
50
+
51
+ Applications can import stylesheets exposed through package `exports` in the same way as local
52
+ stylesheets:
53
+
54
+ ```js
55
+ import "@scope/design-system/tokens.css";
56
+ import "@scope/design-system/theme.css";
57
+ ```
58
+
59
+ Evolit resolves package subpaths with ESM `import` conditions, emits exported CSS through the
60
+ route static-asset pipeline, and adds the resulting stylesheet URLs to the rendered document.
61
+ Relative `@import` rules and `url(...)` references inside package CSS are emitted and rewritten
62
+ from their location within `node_modules`. Bare package imports that resolve to JavaScript continue
63
+ to use the shared vendor runtime; hydration metadata uses that same canonical package URL so an
64
+ external component is evaluated only once. CSS and other static assets do not enter vendor chunks.
65
+
66
+ Packages declared by the application remain package dependencies even when a workspace manager
67
+ links them to sources elsewhere in a monorepo. Evolit uses the declared package name and its
68
+ browser `exports` subpath as the public identity; the physical symlink or real filesystem path is
69
+ used only to verify package ownership and is never exposed as an `__unmanaged__` client module.
70
+ During `evolit build`, a package reached by the production SSR graph that is declared only in
71
+ `devDependencies` produces a warning. Its package identity is preserved, but applications should
72
+ move it to `dependencies` or keep development dependencies installed in the production runtime.
73
+
49
74
  ## Commands
50
75
 
51
76
  ```sh
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "evolit",
3
- "version": "0.1.3",
3
+ "version": "0.1.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",
package/src/build.js CHANGED
@@ -10,6 +10,8 @@ import {
10
10
  import {
11
11
  collectTransitiveAssetPreloads,
12
12
  collectTransitiveStyleUrls,
13
+ buildSharedVendorRuntime,
14
+ canonicalizePackageModuleId,
13
15
  createAssetResolver,
14
16
  createHydrationBootstrap,
15
17
  createStaticAssetPublicUrlMap,
@@ -70,6 +72,31 @@ const CONTENT_TYPE_BY_EXTENSION = new Map([
70
72
  [".js", "text/javascript; charset=utf-8"],
71
73
  ]);
72
74
 
75
+ function getBarePackageName(specifier) {
76
+ if (typeof specifier !== "string" || specifier.length === 0) return null;
77
+ const segments = specifier.split("/");
78
+ return specifier.startsWith("@")
79
+ ? segments.length >= 2 ? `${segments[0]}/${segments[1]}` : null
80
+ : segments[0];
81
+ }
82
+
83
+ export function collectSsrDevDependencyWarnings(packageJson, packageSpecifiers) {
84
+ const productionDependencies = new Set([
85
+ ...Object.keys(packageJson?.dependencies ?? {}),
86
+ ...Object.keys(packageJson?.optionalDependencies ?? {}),
87
+ ...Object.keys(packageJson?.peerDependencies ?? {}),
88
+ ]);
89
+ const devDependencies = new Set(Object.keys(packageJson?.devDependencies ?? {}));
90
+ return [...new Set(packageSpecifiers
91
+ .map(getBarePackageName)
92
+ .filter((packageName) => (
93
+ packageName
94
+ && devDependencies.has(packageName)
95
+ && !productionDependencies.has(packageName)
96
+ )))]
97
+ .sort();
98
+ }
99
+
73
100
  function getContentTypeForBuildArtifact(filePath) {
74
101
  return CONTENT_TYPE_BY_EXTENSION.get(path.extname(filePath)) ?? "application/octet-stream";
75
102
  }
@@ -128,12 +155,15 @@ async function writeDeploymentRuntimeEntry(buildRoot) {
128
155
  return runtimeEntryPath;
129
156
  }
130
157
 
131
- export async function buildProject(projectRoot) {
158
+ export async function buildProject(projectRoot, options = {}) {
132
159
  const evolitConfig = await loadEvolitConfig(projectRoot);
133
160
  const extensions = resolveEvolitExtensions(evolitConfig);
134
161
  const extensionClientDescriptors = getExtensionClientDescriptors(extensions);
162
+ const packageClientSpecifiers = new Set(
163
+ extensionClientDescriptors.map((descriptor) => descriptor.module),
164
+ );
135
165
  const sharedVendorOptions = {
136
- additionalEntrySpecifiers: extensionClientDescriptors.map((descriptor) => descriptor.module),
166
+ additionalEntrySpecifiers: [],
137
167
  };
138
168
  const routes = await discoverAppRoutes(projectRoot);
139
169
  const routeHandlers = await discoverAppRouteHandlers(projectRoot);
@@ -144,6 +174,19 @@ export async function buildProject(projectRoot) {
144
174
  const deployHandlers = [];
145
175
  const compiledClientBoundaries = new Map();
146
176
  const inventoriesBySourceEntry = new Map();
177
+ const ssrPackageImports = new Set();
178
+
179
+ async function compileProductionServerEntry(sourcePath) {
180
+ const result = await compileModuleGraph(sourcePath, {
181
+ projectRoot,
182
+ mode: "production",
183
+ sourceMaps: false,
184
+ ssr: true,
185
+ target: "server",
186
+ });
187
+ result.packageImports.forEach((specifier) => ssrPackageImports.add(specifier));
188
+ return result;
189
+ }
147
190
 
148
191
  function getEntryInventory(entryPath) {
149
192
  let inventory = inventoriesBySourceEntry.get(entryPath);
@@ -174,13 +217,7 @@ export async function buildProject(projectRoot) {
174
217
  await ensureDirectory(buildRoot);
175
218
 
176
219
  for (const routeHandler of routeHandlers) {
177
- await compileModuleGraph(routeHandler.handler, {
178
- projectRoot,
179
- mode: "production",
180
- sourceMaps: false,
181
- ssr: true,
182
- target: "server",
183
- });
220
+ await compileProductionServerEntry(routeHandler.handler);
184
221
 
185
222
  const handlerModule = await importCompiledModule(routeHandler.handler, {
186
223
  projectRoot,
@@ -204,22 +241,10 @@ export async function buildProject(projectRoot) {
204
241
  }
205
242
 
206
243
  for (const route of routes) {
207
- await compileModuleGraph(route.page, {
208
- projectRoot,
209
- mode: "production",
210
- sourceMaps: false,
211
- ssr: true,
212
- target: "server",
213
- });
244
+ await compileProductionServerEntry(route.page);
214
245
 
215
246
  for (const layoutPath of route.layouts) {
216
- await compileModuleGraph(layoutPath, {
217
- projectRoot,
218
- mode: "production",
219
- sourceMaps: false,
220
- ssr: true,
221
- target: "server",
222
- });
247
+ await compileProductionServerEntry(layoutPath);
223
248
 
224
249
  }
225
250
 
@@ -228,13 +253,7 @@ export async function buildProject(projectRoot) {
228
253
  ...(route.errorBoundaries ?? []).map((boundary) => boundary.module),
229
254
  ];
230
255
  for (const boundaryPath of new Set(boundaryModules)) {
231
- await compileModuleGraph(boundaryPath, {
232
- projectRoot,
233
- mode: "production",
234
- sourceMaps: false,
235
- ssr: true,
236
- target: "server",
237
- });
256
+ await compileProductionServerEntry(boundaryPath);
238
257
  }
239
258
 
240
259
  const toProjectRelative = (filePath) => path.relative(projectRoot, filePath).split(path.sep).join("/");
@@ -247,13 +266,21 @@ export async function buildProject(projectRoot) {
247
266
  const allAssets = new Set();
248
267
  const allClientBoundaries = new Set();
249
268
  for (const [entryPath, entryInventory] of inventoriesByEntry) {
269
+ const packageBoundarySources = new Set(
270
+ (entryInventory.packageClientBoundaries ?? []).map((entry) => entry.sourcePath),
271
+ );
272
+ for (const packageBoundary of entryInventory.packageClientBoundaries ?? []) {
273
+ packageClientSpecifiers.add(packageBoundary.specifier);
274
+ }
250
275
  serverAssetImportsByEntry[toProjectRelative(entryPath)] = {
251
276
  styles: entryInventory.styles.map((filePath) => getClientStaticAssetModule(projectRoot, filePath)),
252
277
  assets: entryInventory.assets.map((filePath) => getClientStaticAssetModule(projectRoot, filePath)),
253
278
  };
254
279
  entryInventory.styles.forEach((filePath) => allStyles.add(filePath));
255
280
  entryInventory.assets.forEach((filePath) => allAssets.add(filePath));
256
- entryInventory.clientBoundaries.forEach((filePath) => allClientBoundaries.add(filePath));
281
+ entryInventory.clientBoundaries
282
+ .filter((filePath) => !packageBoundarySources.has(filePath))
283
+ .forEach((filePath) => allClientBoundaries.add(filePath));
257
284
  }
258
285
  await emitClientStaticAssets([...allStyles, ...allAssets], {
259
286
  projectRoot,
@@ -268,12 +295,23 @@ export async function buildProject(projectRoot) {
268
295
  }
269
296
  for (const [entryPath, entryInventory] of inventoriesByEntry) {
270
297
  clientBoundariesByEntry[toProjectRelative(entryPath)] = entryInventory.clientBoundaries
298
+ .filter((clientBoundary) => !(entryInventory.packageClientBoundaries ?? [])
299
+ .some((packageBoundary) => packageBoundary.sourcePath === clientBoundary))
271
300
  .map((clientBoundary) => compiledBoundaryModules.get(clientBoundary))
272
301
  .filter(Boolean)
273
302
  .sort();
274
303
  }
275
304
  }
276
305
 
306
+ const projectPackageJson = JSON.parse(await fs.readFile(path.join(projectRoot, "package.json"), "utf8"));
307
+ const warn = options.onWarning ?? console.warn;
308
+ for (const packageName of collectSsrDevDependencyWarnings(projectPackageJson, [...ssrPackageImports])) {
309
+ warn(
310
+ `[evolit] Production SSR imports ${JSON.stringify(packageName)}, but it is declared only in devDependencies. `
311
+ + "Move it to dependencies or ensure production installations include development dependencies.",
312
+ );
313
+ }
314
+
277
315
  const configuredClientBoundaries = evolitConfig.clientBoundaries ?? [];
278
316
  if (!Array.isArray(configuredClientBoundaries)) {
279
317
  throw new Error("Expected clientBoundaries in evolit.config.js to be an array of module specifiers.");
@@ -295,8 +333,10 @@ export async function buildProject(projectRoot) {
295
333
  await compileProductionClientBoundary(sourcePath);
296
334
  }
297
335
 
336
+ sharedVendorOptions.additionalEntrySpecifiers = [...packageClientSpecifiers].sort();
298
337
  const clientAssets = await emitBundledClientAssets(projectRoot, {
299
338
  entryClientModules,
339
+ additionalVendorSpecifiers: sharedVendorOptions.additionalEntrySpecifiers,
300
340
  serverAssetImportsByEntry,
301
341
  clientBoundariesByEntry,
302
342
  });
@@ -306,8 +346,12 @@ export async function buildProject(projectRoot) {
306
346
  const routeResolver = await createRouteResolver(projectRoot, "production", {
307
347
  staticAssetPublicUrls,
308
348
  });
349
+ const sharedRuntime = await buildSharedVendorRuntime(projectRoot, "production", sharedVendorOptions);
350
+ clientAssets.sharedImports = { ...sharedRuntime.imports };
351
+ const packageImports = sharedRuntime.imports;
309
352
  const assetResolver = createAssetResolver(projectRoot, {
310
353
  assetManifest: clientAssets,
354
+ packageImports,
311
355
  });
312
356
  const hydrationModuleUrl = await resolveSharedVendorModuleUrl(
313
357
  projectRoot,
@@ -341,6 +385,33 @@ export async function buildProject(projectRoot) {
341
385
  ].filter((moduleId) => typeof moduleId === "string" && moduleId.length > 0))];
342
386
  const unresolved = [];
343
387
  for (const moduleId of renderedModules) {
388
+ const packageSpecifier = await canonicalizePackageModuleId(
389
+ projectRoot,
390
+ moduleId,
391
+ packageClientSpecifiers,
392
+ );
393
+ if (packageSpecifier) {
394
+ const publicUrl = packageImports[packageSpecifier] ?? null;
395
+ if (!publicUrl) {
396
+ unresolved.push(moduleId);
397
+ continue;
398
+ }
399
+ const previousPublicUrl = assetResolver(moduleId);
400
+ for (const root of result.hydrationData?.roots ?? []) {
401
+ if (root?.moduleId === moduleId) root.moduleId = packageSpecifier;
402
+ }
403
+ if (Array.isArray(result.clientImports)) {
404
+ result.clientImports = result.clientImports.map((value) => (
405
+ value === moduleId || value === previousPublicUrl ? publicUrl : value
406
+ ));
407
+ }
408
+ if (Array.isArray(result.hydrationData?.clientImports)) {
409
+ result.hydrationData.clientImports = result.hydrationData.clientImports.map((value) => (
410
+ value === moduleId || value === previousPublicUrl ? publicUrl : value
411
+ ));
412
+ }
413
+ continue;
414
+ }
344
415
  if (assetResolver(moduleId) || clientAssets.byPublicPath?.[moduleId]) continue;
345
416
  const importerPath = routeResult.boundaryModule
346
417
  ?? routeResult.route?.page
@@ -406,6 +477,7 @@ export async function buildProject(projectRoot) {
406
477
  result.hydrationData,
407
478
  projectRoot,
408
479
  resolveHydrationRootClientImports(result.hydrationData, assetResolver),
480
+ assetResolver,
409
481
  ),
410
482
  assetResolver,
411
483
  hydrationModuleUrl,
@@ -420,6 +492,7 @@ export async function buildProject(projectRoot) {
420
492
  result.hydrationData,
421
493
  projectRoot,
422
494
  resolveHydrationRootClientImports(result.hydrationData, assetResolver),
495
+ assetResolver,
423
496
  ),
424
497
  );
425
498
  },
@@ -144,35 +144,84 @@ export async function resolvePackageRoot(packageName, options = {}) {
144
144
  }
145
145
 
146
146
  const pendingResolution = (async () => {
147
- let packageEntryPath;
148
- try {
149
- packageEntryPath = createRequire(path.join(resolveFrom, "package.json")).resolve(packageName);
150
- } catch {
151
- packageEntryPath = requireFromHere.resolve(packageName);
152
- }
153
- let currentPath = path.dirname(packageEntryPath);
154
-
155
- while (true) {
156
- const packageJsonPath = path.join(currentPath, "package.json");
147
+ const resolvers = [
148
+ createRequire(path.join(resolveFrom, "package.json")),
149
+ requireFromHere,
150
+ ];
157
151
 
152
+ for (const resolver of resolvers) {
158
153
  try {
154
+ const packageJsonPath = resolver.resolve(`${packageName}/package.json`);
159
155
  const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
160
156
  if (packageJson?.name === packageName) {
161
- return { packageRoot: currentPath, packageJson };
157
+ return { packageRoot: path.dirname(packageJsonPath), packageJson };
162
158
  }
163
159
  } catch {
164
- // Keep walking up until we find the owning package root.
160
+ // Some packages intentionally do not export package.json. Resolve the
161
+ // entrypoint below and walk back to its owning manifest instead.
165
162
  }
163
+ }
166
164
 
167
- const parentPath = path.dirname(currentPath);
168
- if (parentPath === currentPath) {
165
+ let packageEntryPath = null;
166
+ let resolutionError = null;
167
+ for (const resolver of resolvers) {
168
+ try {
169
+ packageEntryPath = resolver.resolve(packageName);
169
170
  break;
171
+ } catch (error) {
172
+ resolutionError = error;
170
173
  }
174
+ }
175
+ if (packageEntryPath) {
176
+ let currentPath = path.dirname(packageEntryPath);
177
+
178
+ while (true) {
179
+ const packageJsonPath = path.join(currentPath, "package.json");
171
180
 
172
- currentPath = parentPath;
181
+ try {
182
+ const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
183
+ if (packageJson?.name === packageName) {
184
+ return { packageRoot: currentPath, packageJson };
185
+ }
186
+ } catch {
187
+ // Keep walking up until we find the owning package root.
188
+ }
189
+
190
+ const parentPath = path.dirname(currentPath);
191
+ if (parentPath === currentPath) {
192
+ break;
193
+ }
194
+
195
+ currentPath = parentPath;
196
+ }
173
197
  }
174
198
 
175
- throw new Error(`Unable to resolve package root for ${packageName}`);
199
+ const packageSegments = packageName.split("/");
200
+ const validPackageSegments = packageSegments.length === 1
201
+ || (packageSegments.length === 2 && packageSegments[0].startsWith("@"));
202
+ if (validPackageSegments && packageSegments.every((segment) => segment && segment !== "." && segment !== "..")) {
203
+ let currentPath = resolveFrom;
204
+ while (true) {
205
+ const packageJsonPath = path.join(currentPath, "node_modules", ...packageSegments, "package.json");
206
+ try {
207
+ const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
208
+ if (packageJson?.name === packageName) {
209
+ return {
210
+ packageRoot: await fs.realpath(path.dirname(packageJsonPath)),
211
+ packageJson,
212
+ };
213
+ }
214
+ } catch {
215
+ // Continue through the normal node_modules ancestor lookup.
216
+ }
217
+
218
+ const parentPath = path.dirname(currentPath);
219
+ if (parentPath === currentPath) break;
220
+ currentPath = parentPath;
221
+ }
222
+ }
223
+
224
+ throw resolutionError ?? new Error(`Unable to resolve package root for ${packageName}`);
176
225
  })();
177
226
 
178
227
  packageRootCache.set(cacheKey, pendingResolution);
@@ -190,6 +239,14 @@ function pickBrowserExportTarget(target) {
190
239
  return target;
191
240
  }
192
241
 
242
+ if (Array.isArray(target)) {
243
+ for (const candidate of target) {
244
+ const resolved = pickBrowserExportTarget(candidate);
245
+ if (resolved) return resolved;
246
+ }
247
+ return null;
248
+ }
249
+
193
250
  if (!target || typeof target !== "object") {
194
251
  return null;
195
252
  }
@@ -202,6 +259,33 @@ function pickBrowserExportTarget(target) {
202
259
  ?? null;
203
260
  }
204
261
 
262
+ function resolveBrowserPackageExport(exportsField, exportKey) {
263
+ if (typeof exportsField === "string" || Array.isArray(exportsField)) {
264
+ return exportKey === "." ? pickBrowserExportTarget(exportsField) : null;
265
+ }
266
+ if (!exportsField || typeof exportsField !== "object") {
267
+ return null;
268
+ }
269
+ if (!Object.keys(exportsField).some((key) => key.startsWith("."))) {
270
+ return exportKey === "." ? pickBrowserExportTarget(exportsField) : null;
271
+ }
272
+ if (exportsField[exportKey] != null) {
273
+ return pickBrowserExportTarget(exportsField[exportKey]);
274
+ }
275
+
276
+ for (const [pattern, target] of Object.entries(exportsField)) {
277
+ const wildcardIndex = pattern.indexOf("*");
278
+ if (wildcardIndex < 0) continue;
279
+ const prefix = pattern.slice(0, wildcardIndex);
280
+ const suffix = pattern.slice(wildcardIndex + 1);
281
+ if (!exportKey.startsWith(prefix) || !exportKey.endsWith(suffix)) continue;
282
+ const wildcard = exportKey.slice(prefix.length, exportKey.length - suffix.length);
283
+ const resolved = pickBrowserExportTarget(target);
284
+ if (resolved) return resolved.replace("*", wildcard);
285
+ }
286
+ return null;
287
+ }
288
+
205
289
  export async function resolveBrowserSpecifierFilePath(specifier, options = {}) {
206
290
  if (!isBareSpecifier(specifier)) {
207
291
  return null;
@@ -222,17 +306,15 @@ export async function resolveBrowserSpecifierFilePath(specifier, options = {}) {
222
306
  const { packageName, subpath } = parsedSpecifier;
223
307
  const { packageRoot, packageJson } = await resolvePackageRoot(packageName, { projectRoot: resolveFrom });
224
308
  const exportKey = subpath.length > 0 ? `./${subpath}` : ".";
225
- const exportsField = packageJson.exports;
226
- const exportTarget = pickBrowserExportTarget(
227
- exportsField && typeof exportsField === "object" && Object.keys(exportsField).some((key) => key.startsWith("."))
228
- ? exportsField[exportKey]
229
- : exportKey === "." ? exportsField : null,
230
- );
231
- const fallbackTarget = subpath.length > 0
232
- ? `./${subpath}`
233
- : typeof packageJson.browser === "string"
234
- ? packageJson.browser
235
- : packageJson.module ?? packageJson.main ?? null;
309
+ const hasExports = packageJson.exports != null;
310
+ const exportTarget = resolveBrowserPackageExport(packageJson.exports, exportKey);
311
+ const fallbackTarget = hasExports
312
+ ? null
313
+ : subpath.length > 0
314
+ ? `./${subpath}`
315
+ : typeof packageJson.browser === "string"
316
+ ? packageJson.browser
317
+ : packageJson.module ?? packageJson.main ?? null;
236
318
  let resolvedTarget = exportTarget ?? fallbackTarget;
237
319
  if (resolvedTarget && packageJson.browser && typeof packageJson.browser === "object") {
238
320
  const normalizedTarget = resolvedTarget.startsWith("./")
@@ -264,6 +346,79 @@ export async function resolveBrowserSpecifierFilePath(specifier, options = {}) {
264
346
  }
265
347
  }
266
348
 
349
+ export async function resolveDeclaredPackageNames(projectRoot) {
350
+ try {
351
+ const packageJson = JSON.parse(await fs.readFile(path.join(projectRoot, "package.json"), "utf8"));
352
+ return new Set([
353
+ ...Object.keys(packageJson.dependencies ?? {}),
354
+ ...Object.keys(packageJson.optionalDependencies ?? {}),
355
+ ...Object.keys(packageJson.peerDependencies ?? {}),
356
+ ...Object.keys(packageJson.devDependencies ?? {}),
357
+ ]);
358
+ } catch (error) {
359
+ if (error?.code === "ENOENT") return new Set();
360
+ throw error;
361
+ }
362
+ }
363
+
364
+ export async function canonicalizePackageModuleId(projectRoot, moduleId, packageSpecifiers = []) {
365
+ const declaredPackageNames = await resolveDeclaredPackageNames(projectRoot);
366
+ const specifiers = [...new Set(packageSpecifiers)]
367
+ .filter((specifier) => {
368
+ const parsed = parsePackageSpecifier(specifier);
369
+ return parsed && declaredPackageNames.has(parsed.packageName);
370
+ });
371
+ if (specifiers.includes(moduleId)) {
372
+ return moduleId;
373
+ }
374
+
375
+ let sourcePath = null;
376
+ if (typeof moduleId === "string" && moduleId.startsWith("file:")) {
377
+ try { sourcePath = fileURLToPath(moduleId); } catch { return null; }
378
+ } else if (typeof moduleId === "string" && path.isAbsolute(moduleId)) {
379
+ sourcePath = moduleId;
380
+ }
381
+ if (!sourcePath || sourcePath.startsWith(`${path.sep}_evolit${path.sep}`)) {
382
+ return null;
383
+ }
384
+
385
+ let realSourcePath;
386
+ try {
387
+ realSourcePath = await fs.realpath(sourcePath);
388
+ } catch {
389
+ realSourcePath = path.resolve(sourcePath);
390
+ }
391
+
392
+ const exactMatches = [];
393
+ const packageMatches = [];
394
+ for (const specifier of specifiers) {
395
+ const parsed = parsePackageSpecifier(specifier);
396
+ try {
397
+ const [{ packageRoot }, entryPath] = await Promise.all([
398
+ resolvePackageRoot(parsed.packageName, { projectRoot }),
399
+ resolveBrowserSpecifierFilePath(specifier, { projectRoot }),
400
+ ]);
401
+ const [realPackageRoot, realEntryPath] = await Promise.all([
402
+ fs.realpath(packageRoot),
403
+ fs.realpath(entryPath),
404
+ ]);
405
+ const relativePath = path.relative(realPackageRoot, realSourcePath);
406
+ const belongsToPackage = relativePath === ""
407
+ || (relativePath !== ".."
408
+ && !relativePath.startsWith(`..${path.sep}`)
409
+ && !path.isAbsolute(relativePath));
410
+ if (!belongsToPackage) continue;
411
+ packageMatches.push(specifier);
412
+ if (realEntryPath === realSourcePath) exactMatches.push(specifier);
413
+ } catch {
414
+ // An unrelated or unavailable optional package cannot own this module.
415
+ }
416
+ }
417
+
418
+ if (exactMatches.length === 1) return exactMatches[0];
419
+ return packageMatches.length === 1 ? packageMatches[0] : null;
420
+ }
421
+
267
422
  export function getSharedOutputRoot(projectRoot, mode) {
268
423
  return path.join(
269
424
  projectRoot,
@@ -1082,6 +1237,10 @@ function toPublicHydrationModuleId(projectRoot, moduleId) {
1082
1237
  return `/${relativePath}`;
1083
1238
  }
1084
1239
 
1240
+ if (isBareSpecifier(moduleId)) {
1241
+ return moduleId;
1242
+ }
1243
+
1085
1244
  if (moduleId.startsWith("/")) {
1086
1245
  return moduleId;
1087
1246
  }
@@ -1113,12 +1272,19 @@ function getServerOutputRoot(projectRoot) {
1113
1272
 
1114
1273
  export function createAssetResolver(projectRoot, options = {}) {
1115
1274
  const assetManifest = normalizeClientAssetManifest(options.assetManifest);
1275
+ const packageImports = options.packageImports instanceof Map
1276
+ ? options.packageImports
1277
+ : new Map(Object.entries(options.packageImports ?? {}));
1116
1278
 
1117
1279
  return function assetResolver(moduleId) {
1118
1280
  if (typeof moduleId !== "string" || moduleId.length === 0) {
1119
1281
  return null;
1120
1282
  }
1121
1283
 
1284
+ if (packageImports.has(moduleId)) {
1285
+ return packageImports.get(moduleId);
1286
+ }
1287
+
1122
1288
  let relativeClientModule = null;
1123
1289
  if (path.isAbsolute(moduleId) && !isVirtualClientModuleId(moduleId)) {
1124
1290
  relativeClientModule = toClientModuleRelativePath(projectRoot, moduleId);
@@ -1220,6 +1386,7 @@ export function normalizeHydrationDataForClient(
1220
1386
  hydrationData,
1221
1387
  projectRoot = null,
1222
1388
  additionalClientImports = [],
1389
+ assetResolver = null,
1223
1390
  ) {
1224
1391
  if (!hydrationData || typeof hydrationData !== "object") {
1225
1392
  return hydrationData ?? null;
@@ -1256,7 +1423,9 @@ export function normalizeHydrationDataForClient(
1256
1423
  const clientImports = [...new Set([
1257
1424
  ...(Array.isArray(hydrationData.clientImports) ? hydrationData.clientImports : []),
1258
1425
  ...(Array.isArray(additionalClientImports) ? additionalClientImports : []),
1259
- ].filter((value) => typeof value === "string" && value.length > 0))];
1426
+ ]
1427
+ .map((value) => typeof assetResolver === "function" ? assetResolver(value) ?? value : value)
1428
+ .filter((value) => typeof value === "string" && value.length > 0))];
1260
1429
 
1261
1430
  Object.defineProperties(normalizedHydrationData, {
1262
1431
  payload: {
@@ -1786,10 +1955,15 @@ async function bundleClientAssets(projectRoot, options = {}) {
1786
1955
  const scriptAssetRecords = [];
1787
1956
  let nextRollupCache = options.rollupCache ?? null;
1788
1957
  if (Object.keys(inputEntries).length > 0) {
1789
- const sharedVendorSpecifiers = await collectClientVendorSpecifiers(projectRoot, [], {
1790
- mode,
1791
- entryClientModules: [...entryClientModules],
1792
- });
1958
+ const sharedVendorSpecifiers = [...new Set([
1959
+ ...await collectClientVendorSpecifiers(projectRoot, [], {
1960
+ mode,
1961
+ entryClientModules: [...entryClientModules],
1962
+ }),
1963
+ ...(Array.isArray(options.additionalVendorSpecifiers)
1964
+ ? options.additionalVendorSpecifiers.filter((specifier) => isBareSpecifier(specifier))
1965
+ : []),
1966
+ ])].sort();
1793
1967
  const sharedRuntime = await buildSharedVendorRuntime(projectRoot, mode, {
1794
1968
  additionalEntrySpecifiers: sharedVendorSpecifiers,
1795
1969
  });
@@ -2227,6 +2401,9 @@ export function normalizeClientAssetManifest(manifest) {
2227
2401
  chunks: Array.isArray(manifest.chunks) ? manifest.chunks : [],
2228
2402
  styles: Array.isArray(manifest.styles) ? manifest.styles : [],
2229
2403
  resources: Array.isArray(manifest.resources) ? manifest.resources : [],
2404
+ sharedImports: manifest.sharedImports && typeof manifest.sharedImports === "object"
2405
+ ? manifest.sharedImports
2406
+ : {},
2230
2407
  serverAssetImportsByEntry:
2231
2408
  manifest.serverAssetImportsByEntry && typeof manifest.serverAssetImportsByEntry === "object"
2232
2409
  ? manifest.serverAssetImportsByEntry
package/src/compiler.js CHANGED
@@ -19,6 +19,7 @@ import { ensureDirectory } from "./fs-utils.js";
19
19
 
20
20
  const MODULE_SPECIFIER_PATTERN =
21
21
  /\b(?:import|export)\s+(?:[^"']*?\s+from\s+)?["']([^"']+)["']|\bimport\s*\(\s*["']([^"']+)["']\s*\)/g;
22
+ const CSS_DEPENDENCY_PATTERN = /(?:@import\s+(?:url\(\s*)?["']([^"']+)["']\s*\)?|url\(\s*["']?([^"')]+)["']?\s*\))/g;
22
23
  const RESOLVABLE_IMPORT_EXTENSIONS = [
23
24
  ...MODULE_EXTENSIONS,
24
25
  ...STATIC_ASSET_EXTENSIONS,
@@ -497,7 +498,8 @@ function createStaticAssetStubSource(relativeAssetPath, mode, target = "server",
497
498
  }
498
499
 
499
500
  async function resolveImportPath(importerPath, specifier) {
500
- const basePath = path.resolve(path.dirname(importerPath), specifier);
501
+ const cleanSpecifier = String(specifier).split("?")[0].split("#")[0];
502
+ const basePath = path.resolve(path.dirname(importerPath), cleanSpecifier);
501
503
  const candidates = [basePath];
502
504
 
503
505
  if (!hasResolvableImportExtension(basePath)) {
@@ -521,6 +523,29 @@ async function resolveImportPath(importerPath, specifier) {
521
523
  return null;
522
524
  }
523
525
 
526
+ async function collectStaticAssetGraph(entryPath, collected = new Set()) {
527
+ const normalizedEntryPath = path.resolve(entryPath);
528
+ if (collected.has(normalizedEntryPath) || !isStaticAssetPath(normalizedEntryPath)) {
529
+ return collected;
530
+ }
531
+
532
+ collected.add(normalizedEntryPath);
533
+ if (!isStyleAssetPath(normalizedEntryPath)) {
534
+ return collected;
535
+ }
536
+
537
+ const source = await fs.readFile(normalizedEntryPath, "utf8");
538
+ for (const match of source.matchAll(CSS_DEPENDENCY_PATTERN)) {
539
+ const specifier = match[1] ?? match[2];
540
+ if (!specifier || !isRelativeSpecifier(specifier)) continue;
541
+ const resolvedPath = await resolveImportPath(normalizedEntryPath, specifier);
542
+ if (resolvedPath && isStaticAssetPath(resolvedPath)) {
543
+ await collectStaticAssetGraph(resolvedPath, collected);
544
+ }
545
+ }
546
+ return collected;
547
+ }
548
+
524
549
  async function loadProjectPathAliases(projectRoot) {
525
550
  if (projectPathAliasesCache.has(projectRoot)) return projectPathAliasesCache.get(projectRoot);
526
551
  const pending = (async () => {
@@ -660,12 +685,15 @@ async function resolveProjectPackageImport(projectRoot, importerPath, specifier)
660
685
  try {
661
686
  const packageJson = JSON.parse(await fs.readFile(path.join(packageRoot, "package.json"), "utf8"));
662
687
  const exportKey = parsed.subpath ? `./${parsed.subpath}` : ".";
688
+ const hasExports = packageJson.exports != null;
663
689
  let target = resolvePackageExports(packageJson.exports, exportKey)
664
- ?? (parsed.subpath
665
- ? `./${parsed.subpath}`
666
- : typeof packageJson.browser === "string"
667
- ? packageJson.browser
668
- : packageJson.module ?? packageJson.main);
690
+ ?? (hasExports
691
+ ? null
692
+ : parsed.subpath
693
+ ? `./${parsed.subpath}`
694
+ : typeof packageJson.browser === "string"
695
+ ? packageJson.browser
696
+ : packageJson.module ?? packageJson.main);
669
697
  if (target && packageJson.browser && typeof packageJson.browser === "object") {
670
698
  const normalizedTarget = target.startsWith("./") ? target : `./${target}`;
671
699
  const browserTarget = packageJson.browser[normalizedTarget]
@@ -812,6 +840,7 @@ async function rewriteRelativeSpecifiers({
812
840
  staticAssetFiles,
813
841
  managedSourceRoots,
814
842
  serverExportsByModule,
843
+ packageImports,
815
844
  }) {
816
845
  const magicSource = new MagicString(code);
817
846
  let didRewrite = false;
@@ -828,8 +857,28 @@ async function rewriteRelativeSpecifiers({
828
857
  const aliasedImportPath = isBareSpecifier(specifier)
829
858
  ? await resolveProjectMappedImport(projectRoot, sourcePath, specifier)
830
859
  : null;
860
+ const packageImportPath = isBareSpecifier(specifier) && !aliasedImportPath
861
+ ? await resolveProjectPackageImport(projectRoot, sourcePath, specifier)
862
+ : null;
863
+ const packageAssetPath = packageImportPath && isStaticAssetPath(packageImportPath)
864
+ ? packageImportPath
865
+ : null;
831
866
 
832
- if (target === "client" && isBareSpecifier(specifier) && !aliasedImportPath) {
867
+ if (
868
+ target === "server"
869
+ && isBareSpecifier(specifier)
870
+ && !aliasedImportPath
871
+ && !packageAssetPath
872
+ ) {
873
+ packageImports?.add(specifier);
874
+ }
875
+
876
+ if (
877
+ target === "client"
878
+ && isBareSpecifier(specifier)
879
+ && !aliasedImportPath
880
+ && !packageAssetPath
881
+ ) {
833
882
  const sourceMetadata = moduleMetadata.get(sourcePath) ?? {
834
883
  moduleImports: new Set(),
835
884
  vendorImports: new Set(),
@@ -841,11 +890,13 @@ async function rewriteRelativeSpecifiers({
841
890
  continue;
842
891
  }
843
892
 
844
- if (!isRelativeSpecifier(specifier) && !aliasedImportPath) {
893
+ if (!isRelativeSpecifier(specifier) && !aliasedImportPath && !packageAssetPath) {
845
894
  continue;
846
895
  }
847
896
 
848
- const resolvedImportPath = aliasedImportPath ?? await resolveImportPath(sourcePath, specifier);
897
+ const resolvedImportPath = packageAssetPath
898
+ ?? aliasedImportPath
899
+ ?? await resolveImportPath(sourcePath, specifier);
849
900
  if (!resolvedImportPath) {
850
901
  continue;
851
902
  }
@@ -875,7 +926,10 @@ async function rewriteRelativeSpecifiers({
875
926
  );
876
927
  }
877
928
  } else if (isStaticAssetPath(resolvedImportPath)) {
878
- staticAssetFiles?.add(resolvedImportPath);
929
+ const staticAssetGraph = await collectStaticAssetGraph(resolvedImportPath);
930
+ for (const staticAssetPath of staticAssetGraph) {
931
+ staticAssetFiles?.add(staticAssetPath);
932
+ }
879
933
  const relativeAssetPath = toOutputRelativePath(projectRoot, resolvedImportPath);
880
934
  const assetOutputPath = path.join(outputRoot, relativeAssetPath);
881
935
  const stubOutputPath = `${assetOutputPath}.mjs`;
@@ -888,8 +942,14 @@ async function rewriteRelativeSpecifiers({
888
942
  );
889
943
 
890
944
  if (target === "client") {
891
- await ensureDirectory(path.dirname(assetOutputPath));
892
- await fs.copyFile(resolvedImportPath, assetOutputPath);
945
+ for (const staticAssetPath of staticAssetGraph) {
946
+ const staticAssetOutputPath = path.join(
947
+ outputRoot,
948
+ toOutputRelativePath(projectRoot, staticAssetPath),
949
+ );
950
+ await ensureDirectory(path.dirname(staticAssetOutputPath));
951
+ await fs.copyFile(staticAssetPath, staticAssetOutputPath);
952
+ }
893
953
  const sourceMetadata = moduleMetadata.get(sourcePath) ?? {
894
954
  moduleImports: new Set(),
895
955
  vendorImports: new Set(),
@@ -1102,6 +1162,7 @@ async function compileModuleGraphUncached(entryPath, options = {}) {
1102
1162
  const staticAssetFiles = new Set();
1103
1163
  const moduleMetadata = new Map();
1104
1164
  const serverExportsByModule = new Map();
1165
+ const packageImports = new Set();
1105
1166
  const serverImportQuery = target === "server" && mode === "development"
1106
1167
  ? `t=${Date.now()}`
1107
1168
  : null;
@@ -1171,6 +1232,7 @@ async function compileModuleGraphUncached(entryPath, options = {}) {
1171
1232
  staticAssetFiles,
1172
1233
  managedSourceRoots,
1173
1234
  serverExportsByModule,
1235
+ packageImports,
1174
1236
  });
1175
1237
 
1176
1238
  await fs.writeFile(outputPath, rewritten.code, "utf8");
@@ -1200,6 +1262,7 @@ async function compileModuleGraphUncached(entryPath, options = {}) {
1200
1262
  entrypoint: await compileModule(entryPath),
1201
1263
  outputRoot,
1202
1264
  sourceFiles: [...new Set([...visited.keys(), ...staticAssetFiles])],
1265
+ packageImports: [...packageImports].sort(),
1203
1266
  };
1204
1267
  }
1205
1268
 
@@ -1239,8 +1302,6 @@ export async function compileModuleGraph(entryPath, options = {}) {
1239
1302
  }
1240
1303
  }
1241
1304
 
1242
- const CSS_DEPENDENCY_PATTERN = /(?:@import\s+(?:url\(\s*)?["']([^"']+)["']\s*\)?|url\(\s*["']?([^"')]+)["']?\s*\))/g;
1243
-
1244
1305
  function normalizeProjectRelativePath(projectRoot, filePath) {
1245
1306
  return toOutputRelativePath(projectRoot, filePath).split(path.sep).join("/");
1246
1307
  }
@@ -1266,6 +1327,7 @@ export async function collectClientGraphInventory(entryPaths, options = {}) {
1266
1327
  const projectRoot = path.resolve(options.projectRoot ?? process.cwd());
1267
1328
  const visited = new Set();
1268
1329
  const boundaries = new Set();
1330
+ const packageBoundaries = new Map();
1269
1331
  const styles = new Set();
1270
1332
  const assets = new Set();
1271
1333
 
@@ -1374,7 +1436,10 @@ export async function collectClientGraphInventory(entryPaths, options = {}) {
1374
1436
  if (!resolved) continue;
1375
1437
  if (shouldCompileModule(resolved)) {
1376
1438
  if (isBareSpecifier(specifier) && aliasResolved == null) {
1377
- if (componentImportSpecifiers.has(specifier)) boundaries.add(resolved);
1439
+ if (componentImportSpecifiers.has(specifier)) {
1440
+ boundaries.add(resolved);
1441
+ packageBoundaries.set(specifier, resolved);
1442
+ }
1378
1443
  } else {
1379
1444
  await visit(resolved, true);
1380
1445
  }
@@ -1390,6 +1455,9 @@ export async function collectClientGraphInventory(entryPaths, options = {}) {
1390
1455
  for (const entryPath of entryPaths) await visit(entryPath);
1391
1456
  return {
1392
1457
  clientBoundaries: [...boundaries].sort(),
1458
+ packageClientBoundaries: [...packageBoundaries]
1459
+ .map(([specifier, sourcePath]) => ({ specifier, sourcePath }))
1460
+ .sort((left, right) => left.specifier.localeCompare(right.specifier)),
1393
1461
  styles: [...styles].sort(),
1394
1462
  assets: [...assets].sort(),
1395
1463
  sourceFiles: [...visited].sort(),
@@ -1406,8 +1474,13 @@ export async function emitClientStaticAssets(assetPaths, options = {}) {
1406
1474
  const outputRoot = getTypedOutputRoot(projectRoot, mode, "client");
1407
1475
  const emitted = [];
1408
1476
 
1477
+ const expandedAssetPaths = new Set();
1409
1478
  for (const assetPath of new Set(assetPaths ?? [])) {
1410
1479
  if (!isStaticAssetPath(assetPath)) continue;
1480
+ await collectStaticAssetGraph(assetPath, expandedAssetPaths);
1481
+ }
1482
+
1483
+ for (const assetPath of expandedAssetPaths) {
1411
1484
  const relativePath = normalizeProjectRelativePath(projectRoot, assetPath);
1412
1485
  const outputPath = path.join(outputRoot, relativePath.split("/").join(path.sep));
1413
1486
  await ensureDirectory(path.dirname(outputPath));
@@ -13,6 +13,7 @@ import {
13
13
  import {
14
14
  collectTransitiveAssetPreloads,
15
15
  collectTransitiveStyleUrls,
16
+ canonicalizePackageModuleId,
16
17
  createAssetResolver,
17
18
  createHydrationBootstrap,
18
19
  createStaticAssetPublicUrlMap,
@@ -46,6 +47,20 @@ import {
46
47
  resolveEvolitExtensions,
47
48
  runRequestExtensions,
48
49
  } from "./extensions.js";
50
+
51
+ function isBarePackageModuleId(value) {
52
+ return typeof value === "string"
53
+ && value.length > 0
54
+ && !value.startsWith(".")
55
+ && !value.startsWith("/")
56
+ && !value.startsWith("@/")
57
+ && !value.startsWith("#")
58
+ && !value.startsWith("file:")
59
+ && !value.startsWith("node:")
60
+ && !value.startsWith("data:")
61
+ && !value.startsWith("http:")
62
+ && !value.startsWith("https:");
63
+ }
49
64
  const CONTENT_TYPE_BY_EXTENSION = new Map([
50
65
  [".css", "text/css; charset=utf-8"],
51
66
  [".svg", "image/svg+xml"],
@@ -240,27 +255,32 @@ export async function createRequestRenderer({
240
255
  extensions = [],
241
256
  }) {
242
257
  let currentAssetManifest = normalizeClientAssetManifest(assetManifest);
258
+ const extensionClientDescriptors = getExtensionClientDescriptors(extensions);
259
+ const extensionClientSpecifiers = extensionClientDescriptors.map((descriptor) => descriptor.module);
260
+ const packageClientSpecifiers = new Set(extensionClientSpecifiers);
261
+ const persistedSharedImports = currentAssetManifest?.sharedImports ?? {};
262
+ const packageImports = new Map(Object.entries(persistedSharedImports));
263
+ const packageImportUrls = new Set(packageImports.values());
243
264
  let currentAssetResolver = createAssetResolver(projectRoot, {
244
265
  assetManifest: currentAssetManifest,
266
+ packageImports,
245
267
  });
246
- const extensionClientDescriptors = getExtensionClientDescriptors(extensions);
247
- const extensionClientSpecifiers = extensionClientDescriptors.map((descriptor) => descriptor.module);
248
- const sharedVendorOptions = { additionalEntrySpecifiers: extensionClientSpecifiers };
249
- let currentHydrationModuleUrl = await resolveSharedVendorModuleUrl(
250
- projectRoot,
251
- mode,
268
+ const sharedVendorOptions = { additionalEntrySpecifiers: [...packageClientSpecifiers] };
269
+ const resolveSharedModuleUrl = (specifier, options = sharedVendorOptions) => (
270
+ persistedSharedImports[specifier]
271
+ ?? (mode === "development"
272
+ ? resolveSharedVendorModuleUrl(projectRoot, mode, specifier, options)
273
+ : null)
274
+ );
275
+ let currentHydrationModuleUrl = await resolveSharedModuleUrl(
252
276
  "@litsx/ssr/hydration",
253
- sharedVendorOptions,
254
277
  );
255
- let currentNavigationModuleUrl = await resolveSharedVendorModuleUrl(
256
- projectRoot,
257
- mode,
278
+ let currentNavigationModuleUrl = await resolveSharedModuleUrl(
258
279
  "evolit/navigation",
259
- sharedVendorOptions,
260
280
  );
261
281
  let currentNavigationExtensions = await Promise.all(extensionClientDescriptors.map(async (descriptor) => ({
262
282
  ...descriptor,
263
- module: await resolveSharedVendorModuleUrl(projectRoot, mode, descriptor.module, sharedVendorOptions),
283
+ module: await resolveSharedModuleUrl(descriptor.module),
264
284
  })));
265
285
  const devBundledEntries = new Set();
266
286
  const devPreparedClientModules = new Set();
@@ -359,11 +379,13 @@ export async function createRequestRenderer({
359
379
  retainOutputPaths: devPreviousAssetOutputPaths,
360
380
  serverAssetImportsByEntry: devServerAssetImportsByEntry,
361
381
  clientBoundariesByEntry: devClientBoundariesByEntry,
382
+ additionalVendorSpecifiers: [...packageClientSpecifiers],
362
383
  });
363
384
  currentAssetManifest = bundledClientAssets.manifest;
364
385
  devPreviousAssetOutputPaths = new Set();
365
386
  currentAssetResolver = createAssetResolver(projectRoot, {
366
387
  assetManifest: currentAssetManifest,
388
+ packageImports,
367
389
  });
368
390
  currentHydrationModuleUrl = await resolveSharedVendorModuleUrl(
369
391
  projectRoot,
@@ -405,6 +427,7 @@ export async function createRequestRenderer({
405
427
  return (
406
428
  typeof currentAssetResolver(moduleId) === "string"
407
429
  || typeof currentAssetManifest?.byPublicPath?.[moduleId] === "string"
430
+ || packageImportUrls.has(moduleId)
408
431
  );
409
432
  }
410
433
 
@@ -447,6 +470,46 @@ export async function createRequestRenderer({
447
470
  ].filter((moduleId) => typeof moduleId === "string" && moduleId.length > 0))];
448
471
  const unresolvedModules = [];
449
472
  for (const moduleId of renderedModules) {
473
+ const packageSpecifier = isBarePackageModuleId(moduleId)
474
+ ? moduleId
475
+ : await canonicalizePackageModuleId(projectRoot, moduleId, packageClientSpecifiers);
476
+ if (packageSpecifier) {
477
+ packageClientSpecifiers.add(packageSpecifier);
478
+ sharedVendorOptions.additionalEntrySpecifiers = [...packageClientSpecifiers].sort();
479
+ const publicUrl = packageImports.get(packageSpecifier) ?? await resolveSharedModuleUrl(
480
+ packageSpecifier,
481
+ {
482
+ assetManifest: currentAssetManifest,
483
+ entryClientModules: [...devBundledEntries],
484
+ additionalEntrySpecifiers: sharedVendorOptions.additionalEntrySpecifiers,
485
+ },
486
+ );
487
+ if (publicUrl) {
488
+ const previousPublicUrl = currentAssetResolver(moduleId);
489
+ packageImports.set(packageSpecifier, publicUrl);
490
+ packageImportUrls.add(publicUrl);
491
+ currentAssetResolver = createAssetResolver(projectRoot, {
492
+ assetManifest: currentAssetManifest,
493
+ packageImports,
494
+ });
495
+ for (const root of result.hydrationData?.roots ?? []) {
496
+ if (root?.moduleId === moduleId) root.moduleId = packageSpecifier;
497
+ }
498
+ if (Array.isArray(result.clientImports)) {
499
+ result.clientImports = result.clientImports.map((value) => (
500
+ value === moduleId || value === previousPublicUrl ? publicUrl : value
501
+ ));
502
+ }
503
+ if (Array.isArray(result.hydrationData?.clientImports)) {
504
+ result.hydrationData.clientImports = result.hydrationData.clientImports.map((value) => (
505
+ value === moduleId || value === previousPublicUrl ? publicUrl : value
506
+ ));
507
+ }
508
+ continue;
509
+ }
510
+ unresolvedModules.push(moduleId);
511
+ continue;
512
+ }
450
513
  const sourcePath = await resolveRenderedClientSource(moduleId, routeResult);
451
514
  if (sourcePath) devKnownClientBoundarySources.add(path.resolve(sourcePath));
452
515
  if (hasClientArtifact(moduleId)) continue;
@@ -575,6 +638,7 @@ export async function createRequestRenderer({
575
638
  result.hydrationData,
576
639
  projectRoot,
577
640
  resolveHydrationRootClientImports(result.hydrationData, currentAssetResolver),
641
+ currentAssetResolver,
578
642
  ),
579
643
  assetResolver(moduleId) {
580
644
  return currentAssetResolver(moduleId);
@@ -591,9 +655,10 @@ export async function createRequestRenderer({
591
655
  result.hydrationData,
592
656
  projectRoot,
593
657
  resolveHydrationRootClientImports(
594
- result.hydrationData,
595
- currentAssetResolver,
596
- ),
658
+ result.hydrationData,
659
+ currentAssetResolver,
660
+ ),
661
+ currentAssetResolver,
597
662
  ),
598
663
  );
599
664
  },
@@ -678,6 +743,7 @@ export async function createRequestRenderer({
678
743
  currentAssetManifest = null;
679
744
  currentAssetResolver = createAssetResolver(projectRoot, {
680
745
  assetManifest: currentAssetManifest,
746
+ packageImports,
681
747
  });
682
748
  devRollupCache = null;
683
749
  for (const clientModule of affectedClientModules) {
@@ -726,11 +792,24 @@ export async function createRequestRenderer({
726
792
  ])));
727
793
  const inventory = {
728
794
  clientBoundaries: [...new Set([...inventoriesByEntry.values()].flatMap((entry) => entry.clientBoundaries))].sort(),
795
+ packageClientBoundaries: [...new Map(
796
+ [...inventoriesByEntry.values()]
797
+ .flatMap((entry) => entry.packageClientBoundaries ?? [])
798
+ .map((entry) => [entry.specifier, entry]),
799
+ ).values()].sort((left, right) => left.specifier.localeCompare(right.specifier)),
729
800
  styles: [...new Set([...inventoriesByEntry.values()].flatMap((entry) => entry.styles))].sort(),
730
801
  assets: [...new Set([...inventoriesByEntry.values()].flatMap((entry) => entry.assets))].sort(),
731
802
  sourceFiles: [...new Set([...inventoriesByEntry.values()].flatMap((entry) => entry.sourceFiles))].sort(),
732
803
  };
733
- const clientBoundaries = inventory.clientBoundaries;
804
+ const packageBoundarySources = new Set(
805
+ inventory.packageClientBoundaries.map((entry) => entry.sourcePath),
806
+ );
807
+ for (const packageBoundary of inventory.packageClientBoundaries) {
808
+ packageClientSpecifiers.add(packageBoundary.specifier);
809
+ }
810
+ sharedVendorOptions.additionalEntrySpecifiers = [...packageClientSpecifiers].sort();
811
+ const clientBoundaries = inventory.clientBoundaries
812
+ .filter((clientBoundary) => !packageBoundarySources.has(clientBoundary));
734
813
  for (const clientBoundary of clientBoundaries) {
735
814
  devKnownClientBoundarySources.add(path.resolve(clientBoundary));
736
815
  }
@@ -747,6 +826,8 @@ export async function createRequestRenderer({
747
826
  assets: entryInventory.assets.map((filePath) => getClientStaticAssetModule(projectRoot, filePath)),
748
827
  };
749
828
  devClientBoundariesByEntry[entryKey] = entryInventory.clientBoundaries
829
+ .filter((filePath) => !(entryInventory.packageClientBoundaries ?? [])
830
+ .some((packageBoundary) => packageBoundary.sourcePath === filePath))
750
831
  .map((filePath) => getCompiledClientModule(projectRoot, filePath))
751
832
  .sort();
752
833
  }