@reckona/mreact-router 0.0.86 → 0.0.88

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/src/render.ts CHANGED
@@ -1,11 +1,8 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { AsyncLocalStorage } from "node:async_hooks";
3
3
  import { access, readFile, stat } from "node:fs/promises";
4
- import { dirname, join, relative, sep } from "node:path";
5
- import {
6
- formatDiagnostic,
7
- transform,
8
- } from "@reckona/mreact-compiler";
4
+ import { dirname, join, relative, resolve, sep } from "node:path";
5
+ import { formatDiagnostic, transform } from "@reckona/mreact-compiler";
9
6
  import type {
10
7
  ClientReferenceMetadata,
11
8
  ServerOutputMode,
@@ -178,10 +175,9 @@ export interface RenderAppRequestOptions {
178
175
  serverModuleCacheVersion?: string | undefined;
179
176
  serverSourceFiles?: ReadonlyMap<string, string> | undefined;
180
177
  serverActions?: AppRouterServerActionOptions | undefined;
181
- serverActionReferencesByFile?: ReadonlyMap<
182
- string,
183
- readonly PreparedFormActionReference[]
184
- > | undefined;
178
+ serverActionReferencesByFile?:
179
+ | ReadonlyMap<string, readonly PreparedFormActionReference[]>
180
+ | undefined;
185
181
  skipMiddleware?: boolean | undefined;
186
182
  preload?: AppRouterRenderPreload | undefined;
187
183
  vitePlugins?: readonly PluginOption[] | undefined;
@@ -240,10 +236,13 @@ export async function preloadBuiltRequestModules(options: {
240
236
 
241
237
  if (route.kind === "server") {
242
238
  await loadServerRouteModule({
239
+ appDir: options.appDir,
243
240
  file: route.file,
241
+ importPolicy: options.importPolicy,
244
242
  serverModules: options.serverModules,
245
243
  serverModuleCacheVersion: options.serverModuleCacheVersion,
246
244
  serverSourceFiles: options.serverSourceFiles,
245
+ vitePlugins: options.vitePlugins,
247
246
  });
248
247
  continue;
249
248
  }
@@ -838,7 +837,9 @@ async function renderAppRequestInternal(options: RenderAppRequestOptions): Promi
838
837
 
839
838
  if (matched.route.kind === "metadata") {
840
839
  return await dispatchMetadataRoute({
840
+ appDir: options.appDir,
841
841
  file: matched.route.file,
842
+ importPolicy: options.importPolicy,
842
843
  params: matched.params,
843
844
  request: options.request,
844
845
  route: matched.route,
@@ -851,8 +852,10 @@ async function renderAppRequestInternal(options: RenderAppRequestOptions): Promi
851
852
 
852
853
  if (matched.route.kind === "server") {
853
854
  return await dispatchServerRoute({
855
+ appDir: options.appDir,
854
856
  env: options.env,
855
857
  file: matched.route.file,
858
+ importPolicy: options.importPolicy,
856
859
  params: matched.params,
857
860
  request: options.request,
858
861
  route: matched.route,
@@ -914,12 +917,13 @@ async function renderAppRequestInternal(options: RenderAppRequestOptions): Promi
914
917
  : cachePolicy.revalidateSeconds !== 0;
915
918
  const reloadRouteCache = isNavigationRouteCacheReloadRequest(options.request);
916
919
  phaseStartedAt = renderTimingPhaseStartedAt(timing);
917
- const cachedResponse = !mayUseRouteCache || reloadRouteCache
918
- ? undefined
919
- : await cachedRouteResponse({
920
- cache: options.routeCache,
921
- key: cacheKey,
922
- });
920
+ const cachedResponse =
921
+ !mayUseRouteCache || reloadRouteCache
922
+ ? undefined
923
+ : await cachedRouteResponse({
924
+ cache: options.routeCache,
925
+ key: cacheKey,
926
+ });
923
927
  finishRenderTimingPhase(timing, phaseStartedAt, "routeCacheMs");
924
928
 
925
929
  if (cachedResponse !== undefined) {
@@ -1595,7 +1599,9 @@ function isNavigationRequest(request: Request): boolean {
1595
1599
  }
1596
1600
 
1597
1601
  function isNavigationRouteCacheReloadRequest(request: Request): boolean {
1598
- return isNavigationRequest(request) && request.headers.get("x-mreact-navigation-cache") === "reload";
1602
+ return (
1603
+ isNavigationRequest(request) && request.headers.get("x-mreact-navigation-cache") === "reload"
1604
+ );
1599
1605
  }
1600
1606
 
1601
1607
  async function nearestBoundaryFileForPage(options: {
@@ -1856,8 +1862,10 @@ function errorDebugContext(
1856
1862
  }
1857
1863
 
1858
1864
  async function dispatchServerRoute(options: {
1865
+ appDir: string;
1859
1866
  env?: unknown;
1860
1867
  file: string;
1868
+ importPolicy?: AppRouterImportPolicy | undefined;
1861
1869
  params: RouteParams;
1862
1870
  request: Request;
1863
1871
  route: AppRoute;
@@ -1943,7 +1951,9 @@ function jsonConventionResponse(body: ManifestDescriptor): Response {
1943
1951
  }
1944
1952
 
1945
1953
  async function dispatchMetadataRoute(options: {
1954
+ appDir: string;
1946
1955
  file: string;
1956
+ importPolicy?: AppRouterImportPolicy | undefined;
1947
1957
  params: RouteParams;
1948
1958
  request: Request;
1949
1959
  route: Extract<AppRoute, { kind: "metadata" }>;
@@ -1992,41 +2002,44 @@ async function dispatchMetadataRoute(options: {
1992
2002
  value instanceof Uint8Array
1993
2003
  ? value
1994
2004
  : new TextEncoder().encode(typeof value === "string" ? value : String(value));
1995
- return bytesResponse(
1996
- body,
1997
- {
1998
- headers: {
1999
- "cache-control": "public, max-age=3600",
2000
- "content-type": typeof value === "string" && value.trimStart().startsWith("<svg")
2005
+ return bytesResponse(body, {
2006
+ headers: {
2007
+ "cache-control": "public, max-age=3600",
2008
+ "content-type":
2009
+ typeof value === "string" && value.trimStart().startsWith("<svg")
2001
2010
  ? "image/svg+xml"
2002
2011
  : "application/octet-stream",
2003
- },
2004
2012
  },
2005
- );
2013
+ });
2006
2014
  }
2007
2015
 
2008
2016
  return new Response("Invalid metadata route convention", { status: 500 });
2009
2017
  }
2010
2018
 
2011
2019
  async function loadServerRouteModule(options: {
2020
+ appDir: string;
2012
2021
  file: string;
2022
+ importPolicy?: AppRouterImportPolicy | undefined;
2013
2023
  serverModules?: ReadonlyMap<string, BuiltServerModuleArtifact> | undefined;
2014
2024
  serverModuleCacheVersion?: string | undefined;
2015
2025
  serverSourceFiles?: ReadonlyMap<string, string> | undefined;
2016
2026
  vitePlugins?: readonly PluginOption[] | undefined;
2017
2027
  }): Promise<Record<string, unknown>> {
2028
+ const code = await readServerSourceFile(
2029
+ options.file,
2030
+ options.serverModuleCacheVersion,
2031
+ options.serverSourceFiles,
2032
+ );
2033
+ const externalSourceDirs = devExternalSourceDirs(options.file, options.importPolicy);
2034
+
2018
2035
  if (
2019
2036
  options.serverModuleCacheVersion === undefined &&
2020
- (options.vitePlugins === undefined || options.vitePlugins.length === 0)
2037
+ (options.vitePlugins === undefined || options.vitePlugins.length === 0) &&
2038
+ (externalSourceDirs === undefined || !hasRelativeSourceImport(code))
2021
2039
  ) {
2022
2040
  return await importAppRouterFileModule<Record<string, unknown>>(options.file);
2023
2041
  }
2024
2042
 
2025
- const code = await readServerSourceFile(
2026
- options.file,
2027
- options.serverModuleCacheVersion,
2028
- options.serverSourceFiles,
2029
- );
2030
2043
  const artifactCode = options.serverModules?.get(options.file)?.request;
2031
2044
  const codeHash = memoizedHashText(code);
2032
2045
  if (
@@ -2057,7 +2070,15 @@ async function loadServerRouteModule(options: {
2057
2070
  const loaded = importAppRouterSourceModule<Record<string, unknown>>({
2058
2071
  cacheKey,
2059
2072
  code: moduleCode,
2073
+ externalizeAppSourceModuleDirs: externalSourceDirs,
2060
2074
  label: `server-route:${options.file}`,
2075
+ plugins: [
2076
+ createAppRouterImportPolicyPlugin({
2077
+ appDir: options.appDir,
2078
+ importPolicy: options.importPolicy,
2079
+ label: "Route handler",
2080
+ }),
2081
+ ],
2061
2082
  ...(moduleCode === code ? { resolveDir: dirname(options.file) } : {}),
2062
2083
  sourcefile: options.file,
2063
2084
  vitePlugins: options.vitePlugins,
@@ -2076,6 +2097,25 @@ async function loadServerRouteModule(options: {
2076
2097
  return loaded;
2077
2098
  }
2078
2099
 
2100
+ function hasRelativeSourceImport(code: string): boolean {
2101
+ return (
2102
+ /\b(?:import|export)\s+(?:[^"'()]*?\s+from\s+)?["']\.{1,2}\//u.test(code) ||
2103
+ /\bimport\s*\(\s*["']\.{1,2}\//u.test(code)
2104
+ );
2105
+ }
2106
+
2107
+ function devExternalSourceDirs(
2108
+ baseDir: string,
2109
+ importPolicy: AppRouterImportPolicy | undefined,
2110
+ ): readonly string[] | undefined {
2111
+ if (importPolicy?.allowedSourceDirs === undefined) {
2112
+ return undefined;
2113
+ }
2114
+
2115
+ const projectRoot = resolve(importPolicy.projectRoot ?? baseDir);
2116
+ return importPolicy.allowedSourceDirs.map((directory) => resolve(projectRoot, directory));
2117
+ }
2118
+
2079
2119
  async function runMiddleware(options: {
2080
2120
  appDir: string;
2081
2121
  importPolicy?: AppRouterImportPolicy | undefined;
@@ -3871,12 +3911,14 @@ async function loadRouteLoaderModule(options: {
3871
3911
  export async function bundleRouteLoaderModuleCode(options: {
3872
3912
  appDir: string;
3873
3913
  code: string;
3914
+ externalizeAppSourceModuleDirs?: readonly string[] | undefined;
3874
3915
  filename: string;
3875
3916
  importPolicy?: AppRouterImportPolicy | undefined;
3876
3917
  vitePlugins?: readonly PluginOption[] | undefined;
3877
3918
  }): Promise<string> {
3878
3919
  const output = await bundleRouterModule({
3879
3920
  code: stripRouteLoaderOnlyExports(options.code),
3921
+ externalizeAppSourceModuleDirs: options.externalizeAppSourceModuleDirs,
3880
3922
  filename: options.filename,
3881
3923
  platform: "node",
3882
3924
  vitePlugins: options.vitePlugins,
@@ -3920,6 +3962,10 @@ async function loadBundledRouteLoaderModule(options: {
3920
3962
  (await bundleRouteLoaderModuleCode({
3921
3963
  appDir: options.appDir,
3922
3964
  code: options.code,
3965
+ externalizeAppSourceModuleDirs:
3966
+ options.serverModuleCacheVersion === undefined
3967
+ ? devExternalSourceDirs(options.appDir, options.importPolicy)
3968
+ : undefined,
3923
3969
  filename: options.filename,
3924
3970
  importPolicy: options.importPolicy,
3925
3971
  vitePlugins: options.vitePlugins,
package/src/vite.ts CHANGED
@@ -6,7 +6,13 @@ import {
6
6
  createCompilerModuleContext,
7
7
  transformCompilerModuleContext,
8
8
  } from "@reckona/mreact-compiler/internal";
9
- import { normalizePath, type Connect, type Plugin, type PluginOption, type ViteDevServer } from "vite";
9
+ import {
10
+ normalizePath,
11
+ type Connect,
12
+ type Plugin,
13
+ type PluginOption,
14
+ type ViteDevServer,
15
+ } from "vite";
10
16
  import type { AppRouterServerActionOptions } from "./actions.js";
11
17
  import type { AppRouterCache } from "./cache.js";
12
18
  import {
@@ -69,11 +75,11 @@ type MreactRouterPlugin = Plugin & {
69
75
  [mreactRouterConfigKey]: ResolvedAppRouterProject;
70
76
  };
71
77
 
72
- export function createAppRouterVitePlugin(
73
- options: AppRouterVitePluginOptions,
74
- ): Plugin {
78
+ export function createAppRouterVitePlugin(options: AppRouterVitePluginOptions): Plugin {
75
79
  const project = resolveAppRouterProjectOptions(options);
76
- const normalizedSourceDirs = project.allowedSourceDirs.map((directory) => normalizePath(directory));
80
+ const normalizedSourceDirs = project.allowedSourceDirs.map((directory) =>
81
+ normalizePath(directory),
82
+ );
77
83
  const packageFile = (monorepoDir: string, packageName: string, entry: string): string =>
78
84
  workspacePackageFile({
79
85
  currentFileUrl: import.meta.url,
@@ -94,18 +100,21 @@ export function createAppRouterVitePlugin(
94
100
  "@reckona/mreact-compat",
95
101
  ];
96
102
  const runtimePaths = new Map([
103
+ ["react", reactCompatPath],
104
+ ["react-dom", reactCompatPath],
105
+ ["react-dom/client", reactCompatPath],
106
+ ["react-dom/server", reactCompatPath],
97
107
  [
98
- "@reckona/mreact-reactive-core/internal",
99
- packageFile("reactive-core", "@reckona/mreact-reactive-core", "internal"),
100
- ],
101
- [
102
- "@reckona/mreact-reactive-dom",
103
- reactiveDomPath,
108
+ "react/jsx-dev-runtime",
109
+ packageFile("react-compat", "@reckona/mreact-compat", "jsx-dev-runtime"),
104
110
  ],
111
+ ["react/jsx-runtime", packageFile("react-compat", "@reckona/mreact-compat", "jsx-runtime")],
105
112
  [
106
- "@reckona/mreact-compat",
107
- reactCompatPath,
113
+ "@reckona/mreact-reactive-core/internal",
114
+ packageFile("reactive-core", "@reckona/mreact-reactive-core", "internal"),
108
115
  ],
116
+ ["@reckona/mreact-reactive-dom", reactiveDomPath],
117
+ ["@reckona/mreact-compat", reactCompatPath],
109
118
  [
110
119
  "@reckona/mreact-compat/event-priority",
111
120
  packageFile("react-compat", "@reckona/mreact-compat", "event-priority"),
@@ -130,10 +139,7 @@ export function createAppRouterVitePlugin(
130
139
  "@reckona/mreact-compat/scheduler",
131
140
  packageFile("react-compat", "@reckona/mreact-compat", "scheduler"),
132
141
  ],
133
- [
134
- "@reckona/mreact-router/link",
135
- packageFile("router", "@reckona/mreact-router", "link"),
136
- ],
142
+ ["@reckona/mreact-router/link", packageFile("router", "@reckona/mreact-router", "link")],
137
143
  [
138
144
  "@reckona/mreact-router/navigation-state",
139
145
  packageFile("router", "@reckona/mreact-router", "navigation-state"),
@@ -148,6 +154,20 @@ export function createAppRouterVitePlugin(
148
154
  [mreactRouterConfigKey]: project,
149
155
  enforce: "pre",
150
156
  name: "mreact-router",
157
+ config() {
158
+ return {
159
+ optimizeDeps: {
160
+ exclude: [
161
+ "react",
162
+ "react-dom",
163
+ "react-dom/client",
164
+ "react-dom/server",
165
+ "react/jsx-dev-runtime",
166
+ "react/jsx-runtime",
167
+ ],
168
+ },
169
+ };
170
+ },
151
171
  configureServer(server) {
152
172
  server.middlewares.use(createDevCssProxyMiddleware());
153
173
 
@@ -158,9 +178,7 @@ export function createAppRouterVitePlugin(
158
178
  vitePlugins: server.config.plugins,
159
179
  };
160
180
 
161
- server.middlewares.use(
162
- createAppRouterViteMiddleware(middlewareOptions),
163
- );
181
+ server.middlewares.use(createAppRouterViteMiddleware(middlewareOptions));
164
182
  };
165
183
  },
166
184
  handleHotUpdate(context) {
@@ -207,11 +225,9 @@ export function currentDevtoolsEmitter() { return undefined; }`;
207
225
  }
208
226
 
209
227
  if (id.startsWith(virtualClientPrefix)) {
210
- return renderAppRouterClientAsset(
211
- project.routesDir,
212
- id.slice(virtualClientPrefix.length),
213
- { dev: true },
214
- ).then(async (response) => {
228
+ return renderAppRouterClientAsset(project.routesDir, id.slice(virtualClientPrefix.length), {
229
+ dev: true,
230
+ }).then(async (response) => {
215
231
  if (!response.ok) {
216
232
  const message = await response.text();
217
233
  throw new Error(message || `MReact client route asset was not found: ${id}`);
@@ -259,9 +275,7 @@ export function currentDevtoolsEmitter() { return undefined; }`;
259
275
 
260
276
  if (output.diagnostics.length > 0) {
261
277
  throw new Error(
262
- output.diagnostics
263
- .map((diagnostic) => formatDiagnostic(filename, diagnostic))
264
- .join("\n"),
278
+ output.diagnostics.map((diagnostic) => formatDiagnostic(filename, diagnostic)).join("\n"),
265
279
  );
266
280
  }
267
281
 
@@ -336,11 +350,7 @@ export function mreactRouterConfigFromPlugins(
336
350
  plugins: readonly unknown[],
337
351
  ): ResolvedAppRouterProject | undefined {
338
352
  for (const plugin of plugins.flat(Infinity)) {
339
- if (
340
- plugin !== null &&
341
- typeof plugin === "object" &&
342
- mreactRouterConfigKey in plugin
343
- ) {
353
+ if (plugin !== null && typeof plugin === "object" && mreactRouterConfigKey in plugin) {
344
354
  return (plugin as MreactRouterPlugin)[mreactRouterConfigKey];
345
355
  }
346
356
  }
@@ -602,9 +612,7 @@ function importerInRuntimePackage(
602
612
  const normalizedImporter = normalizePath(importer);
603
613
  return (
604
614
  directories.some((directory) => normalizedImporter.startsWith(`${directory}/`)) ||
605
- packageNames.some((packageName) =>
606
- normalizedImporter.includes(`/node_modules/${packageName}/`),
607
- )
615
+ packageNames.some((packageName) => normalizedImporter.includes(`/node_modules/${packageName}/`))
608
616
  );
609
617
  }
610
618
 
@@ -661,7 +669,9 @@ async function devNavigationScripts(appDir: string): Promise<ReadonlyMap<string,
661
669
  }),
662
670
  );
663
671
 
664
- return new Map(entries.filter((entry): entry is readonly [string, string] => entry !== undefined));
672
+ return new Map(
673
+ entries.filter((entry): entry is readonly [string, string] => entry !== undefined),
674
+ );
665
675
  }
666
676
 
667
677
  function createDevCssProxyMiddleware(): Connect.NextHandleFunction {