@expo/cli 54.0.10 → 54.1.0-canary-20251003-7b9d7ff

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.
Files changed (30) hide show
  1. package/build/bin/cli +1 -1
  2. package/build/src/run/ios/runIosAsync.js +0 -8
  3. package/build/src/run/ios/runIosAsync.js.map +1 -1
  4. package/build/src/start/interface/cliExtensionMenuItemHandler.js +173 -0
  5. package/build/src/start/interface/cliExtensionMenuItemHandler.js.map +1 -0
  6. package/build/src/start/interface/createDevToolsMenuItems.js +159 -0
  7. package/build/src/start/interface/createDevToolsMenuItems.js.map +1 -0
  8. package/build/src/start/interface/interactiveActions.js +8 -12
  9. package/build/src/start/interface/interactiveActions.js.map +1 -1
  10. package/build/src/start/platforms/android/adb.js +1 -1
  11. package/build/src/start/platforms/android/adb.js.map +1 -1
  12. package/build/src/start/server/DevToolsPlugin.js +60 -0
  13. package/build/src/start/server/DevToolsPlugin.js.map +1 -0
  14. package/build/src/start/server/DevToolsPlugin.schema.js +79 -0
  15. package/build/src/start/server/DevToolsPlugin.schema.js.map +1 -0
  16. package/build/src/start/server/DevToolsPluginCliExtensionExecutor.js +119 -0
  17. package/build/src/start/server/DevToolsPluginCliExtensionExecutor.js.map +1 -0
  18. package/build/src/start/server/DevToolsPluginCliExtensionResults.js +61 -0
  19. package/build/src/start/server/DevToolsPluginCliExtensionResults.js.map +1 -0
  20. package/build/src/start/server/DevToolsPluginManager.js +4 -8
  21. package/build/src/start/server/DevToolsPluginManager.js.map +1 -1
  22. package/build/src/start/server/metro/MetroBundlerDevServer.js +125 -9
  23. package/build/src/start/server/metro/MetroBundlerDevServer.js.map +1 -1
  24. package/build/src/start/server/metro/createServerRouteMiddleware.js +2 -2
  25. package/build/src/start/server/metro/createServerRouteMiddleware.js.map +1 -1
  26. package/build/src/start/server/middleware/DataLoaderModuleMiddleware.js +75 -0
  27. package/build/src/start/server/middleware/DataLoaderModuleMiddleware.js.map +1 -0
  28. package/build/src/utils/telemetry/clients/FetchClient.js +1 -1
  29. package/build/src/utils/telemetry/utils/context.js +1 -1
  30. package/package.json +20 -20
@@ -108,6 +108,7 @@ const _BundlerDevServer = require("../BundlerDevServer");
108
108
  const _getStaticRenderFunctions = require("../getStaticRenderFunctions");
109
109
  const _ContextModuleSourceMapsMiddleware = require("../middleware/ContextModuleSourceMapsMiddleware");
110
110
  const _CreateFileMiddleware = require("../middleware/CreateFileMiddleware");
111
+ const _DataLoaderModuleMiddleware = require("../middleware/DataLoaderModuleMiddleware");
111
112
  const _DevToolsPluginMiddleware = require("../middleware/DevToolsPluginMiddleware");
112
113
  const _DomComponentsMiddleware = require("../middleware/DomComponentsMiddleware");
113
114
  const _FaviconMiddleware = require("../middleware/FaviconMiddleware");
@@ -319,7 +320,9 @@ class MetroBundlerDevServer extends _BundlerDevServer.BundlerDevServer {
319
320
  })
320
321
  };
321
322
  }
322
- async getStaticRenderFunctionAsync() {
323
+ /**
324
+ * This function is invoked when exporting via `expo export`
325
+ */ async getStaticRenderFunctionAsync() {
323
326
  var _exp_extra, _exp_extra1;
324
327
  const url = this.getDevServerUrlOrAssert();
325
328
  const { getStaticContent, getManifest, getBuildTimeServerManifestAsync } = await this.ssrLoadModule('expo-router/node/render.js', {
@@ -328,10 +331,11 @@ class MetroBundlerDevServer extends _BundlerDevServer.BundlerDevServer {
328
331
  environment: 'node'
329
332
  });
330
333
  const { exp } = (0, _config().getConfig)(this.projectRoot);
334
+ const serverManifest = await getBuildTimeServerManifestAsync({
335
+ ...(_exp_extra = exp.extra) == null ? void 0 : _exp_extra.router
336
+ });
331
337
  return {
332
- serverManifest: await getBuildTimeServerManifestAsync({
333
- ...(_exp_extra = exp.extra) == null ? void 0 : _exp_extra.router
334
- }),
338
+ serverManifest,
335
339
  // Get routes from Expo Router.
336
340
  manifest: await getManifest({
337
341
  preserveApiRoutes: false,
@@ -367,7 +371,10 @@ class MetroBundlerDevServer extends _BundlerDevServer.BundlerDevServer {
367
371
  bytecode: false
368
372
  });
369
373
  }
370
- async getStaticPageAsync(pathname) {
374
+ /**
375
+ * This function is invoked when running in development via `expo start`
376
+ */ async getStaticPageAsync(pathname, route) {
377
+ const { exp } = (0, _config().getConfig)(this.projectRoot);
371
378
  const { mode, isExporting, clientBoundaries, baseUrl, reactCompiler, routerRoot, asyncRoutes } = this.instanceMetroOptions;
372
379
  (0, _assert().default)(mode != null && isExporting != null && baseUrl != null && reactCompiler != null && routerRoot != null && asyncRoutes != null, 'The server must be started before calling getStaticPageAsync.');
373
380
  const platform = 'web';
@@ -389,6 +396,7 @@ class MetroBundlerDevServer extends _BundlerDevServer.BundlerDevServer {
389
396
  bytecode: false
390
397
  });
391
398
  const bundleStaticHtml = async ()=>{
399
+ var _exp_extra_router, _exp_extra;
392
400
  const { getStaticContent } = await this.ssrLoadModule('expo-router/node/render.js', {
393
401
  // This must always use the legacy rendering resolution (no `react-server`) because it leverages
394
402
  // the previous React SSG utilities which aren't available in React 19.
@@ -398,7 +406,17 @@ class MetroBundlerDevServer extends _BundlerDevServer.BundlerDevServer {
398
406
  platform
399
407
  });
400
408
  const location = new URL(pathname, this.getDevServerUrlOrAssert());
401
- return await getStaticContent(location);
409
+ const useServerDataLoaders = (_exp_extra = exp.extra) == null ? void 0 : (_exp_extra_router = _exp_extra.router) == null ? void 0 : _exp_extra_router.unstable_useServerDataLoaders;
410
+ if (useServerDataLoaders) {
411
+ const data = await this.executeServerDataLoaderAsync(location, route);
412
+ return await getStaticContent(location, {
413
+ loader: {
414
+ data
415
+ }
416
+ });
417
+ } else {
418
+ return await getStaticContent(location);
419
+ }
402
420
  };
403
421
  const [{ artifacts: resources }, staticHtml] = await Promise.all([
404
422
  this.getStaticResourcesAsync({
@@ -806,6 +824,14 @@ class MetroBundlerDevServer extends _BundlerDevServer.BundlerDevServer {
806
824
  middleware.use(new _CreateFileMiddleware.CreateFileMiddleware(this.projectRoot).getHandler());
807
825
  // Append support for redirecting unhandled requests to the index.html page on web.
808
826
  if (this.isTargetingWeb()) {
827
+ var _exp_extra_router1, _exp_extra2;
828
+ if ((_exp_extra2 = exp.extra) == null ? void 0 : (_exp_extra_router1 = _exp_extra2.router) == null ? void 0 : _exp_extra_router1.unstable_useServerDataLoaders) {
829
+ const loaderModuleMiddleware = new _DataLoaderModuleMiddleware.DataLoaderModuleMiddleware(this.projectRoot, appDir, async (location, route)=>{
830
+ return this.executeServerDataLoaderAsync(location, route);
831
+ }, ()=>this.getDevServerUrlOrAssert());
832
+ // This MUST be before ServeStaticMiddleware so it doesn't treat the loader files as static assets
833
+ middleware.use(loaderModuleMiddleware.getHandler());
834
+ }
809
835
  // This MUST be after the manifest middleware so it doesn't have a chance to serve the template `public/index.html`.
810
836
  middleware.use(new _ServeStaticMiddleware.ServeStaticMiddleware(this.projectRoot).getHandler());
811
837
  // This should come after the static middleware so it doesn't serve the favicon from `public/favicon.ico`.
@@ -816,6 +842,7 @@ class MetroBundlerDevServer extends _BundlerDevServer.BundlerDevServer {
816
842
  metro,
817
843
  server
818
844
  }, (events)=>{
845
+ var _exp_extra_router, _exp_extra;
819
846
  if (hasApiRoutes) {
820
847
  // NOTE(EvanBacon): We aren't sure what files the API routes are using so we'll just invalidate
821
848
  // aggressively to ensure we always have the latest. The only caching we really get here is for
@@ -834,6 +861,15 @@ class MetroBundlerDevServer extends _BundlerDevServer.BundlerDevServer {
834
861
  }
835
862
  }
836
863
  }
864
+ // Handle loader file changes for HMR
865
+ if ((_exp_extra = exp.extra) == null ? void 0 : (_exp_extra_router = _exp_extra.router) == null ? void 0 : _exp_extra_router.unstable_useServerDataLoaders) {
866
+ for (const event of events){
867
+ var _event_metadata1;
868
+ if (((_event_metadata1 = event.metadata) == null ? void 0 : _event_metadata1.type) !== 'd') {
869
+ this.handleLoaderFileChange(event.filePath);
870
+ }
871
+ }
872
+ }
837
873
  });
838
874
  }
839
875
  // If React 19 is enabled, then add RSC middleware to the dev server.
@@ -867,7 +903,7 @@ class MetroBundlerDevServer extends _BundlerDevServer.BundlerDevServer {
867
903
  bundleApiRoute: (functionFilePath)=>this.ssrImportApiRoute(functionFilePath, {
868
904
  platform: 'web'
869
905
  }),
870
- getStaticPageAsync: async (pathname)=>{
906
+ getStaticPageAsync: async (pathname, route)=>{
871
907
  // TODO: Add server rendering when RSC is enabled.
872
908
  if (isReactServerComponentsEnabled) {
873
909
  // NOTE: This is a temporary hack to return the SPA/template index.html in development when RSC is enabled.
@@ -878,7 +914,7 @@ class MetroBundlerDevServer extends _BundlerDevServer.BundlerDevServer {
878
914
  };
879
915
  }
880
916
  // Non-RSC apps will bundle the static HTML for a given pathname and respond with it.
881
- return this.getStaticPageAsync(pathname);
917
+ return this.getStaticPageAsync(pathname, route);
882
918
  }
883
919
  }));
884
920
  }
@@ -1112,6 +1148,62 @@ class MetroBundlerDevServer extends _BundlerDevServer.BundlerDevServer {
1112
1148
  invalidateApiRouteCache() {
1113
1149
  this.pendingRouteOperations.clear();
1114
1150
  }
1151
+ /**
1152
+ * Execute a route's loader function. Used during SSR/SSG to fetch data required by routes.
1153
+ *
1154
+ * This function is used during development and production builds, and **must** receive a valid
1155
+ * matched route.
1156
+ *
1157
+ * @experimental
1158
+ */ async executeServerDataLoaderAsync(location, route) {
1159
+ var _exp_extra;
1160
+ const { exp } = (0, _config().getConfig)(this.projectRoot);
1161
+ const { unstable_useServerDataLoaders } = (_exp_extra = exp.extra) == null ? void 0 : _exp_extra.router;
1162
+ if (!unstable_useServerDataLoaders) {
1163
+ throw new _errors.CommandError('LOADERS_NOT_ENABLED', 'Server data loaders are not enabled. Add `unstable_useServerDataLoaders` to your `expo-router` plugin config.');
1164
+ }
1165
+ // If the route is unmatched, we can ignore it
1166
+ // TODO(@hassankhan): Is this the best way of determining the route is unmatched?
1167
+ if (route.page === '/+not-found') {
1168
+ return;
1169
+ }
1170
+ const { routerRoot } = this.instanceMetroOptions;
1171
+ (0, _assert().default)(routerRoot != null, 'The server must be started before calling executeRouteLoaderAsync.');
1172
+ let loaderData;
1173
+ try {
1174
+ debug('Matched route loader to file: ', route.file);
1175
+ // TODO(@hassankhan): This should move to a util function
1176
+ const params = {};
1177
+ const match = route.namedRegex.exec(location.pathname);
1178
+ if (match == null ? void 0 : match.groups) {
1179
+ for (const [key, value] of Object.entries(match.groups)){
1180
+ const namedKey = route.routeKeys[key];
1181
+ params[namedKey] = value;
1182
+ }
1183
+ }
1184
+ let modulePath = route.file;
1185
+ const appDir = _path().default.join(this.projectRoot, routerRoot);
1186
+ modulePath = _path().default.isAbsolute(modulePath) ? modulePath : _path().default.join(appDir, modulePath);
1187
+ modulePath = modulePath.replace(/\.(js|ts)x?$/, '');
1188
+ debug('Using loader module path: ', modulePath);
1189
+ const routeModule = await this.ssrLoadModule(modulePath, {
1190
+ environment: 'node'
1191
+ });
1192
+ if (routeModule.loader) {
1193
+ // Register this module for loader HMR
1194
+ this.setupLoaderHmr(modulePath);
1195
+ loaderData = await routeModule.loader({
1196
+ params,
1197
+ // NOTE(@hassankhan): The `request` object should only be available when using SSR
1198
+ request: null
1199
+ });
1200
+ }
1201
+ } catch (error) {
1202
+ throw new _errors.CommandError('LOADER_EXECUTION_FAILED', `Failed to execute loader for route "${location.pathname}": ${error.message}`);
1203
+ }
1204
+ debug('Loader data:', loaderData, ' for location:', location.pathname);
1205
+ return loaderData;
1206
+ }
1115
1207
  // Ensure the global is available for SSR CSS modules to inject client updates.
1116
1208
  bindRSCDevModuleInjectionHandler() {
1117
1209
  // Used by SSR CSS modules to broadcast client updates.
@@ -1150,6 +1242,30 @@ class MetroBundlerDevServer extends _BundlerDevServer.BundlerDevServer {
1150
1242
  };
1151
1243
  this.registerSsrHmrAsync(url.toString(), onReload);
1152
1244
  }
1245
+ setupLoaderHmr(modulePath) {
1246
+ if (this.watchedLoaderFiles.has(modulePath)) {
1247
+ return;
1248
+ }
1249
+ this.watchedLoaderFiles.add(modulePath);
1250
+ debug('[Loader HMR] Registering loader file for HMR:', modulePath);
1251
+ }
1252
+ handleLoaderFileChange(changedFilePath) {
1253
+ for (const loaderPath of this.watchedLoaderFiles){
1254
+ const possibleExtensions = [
1255
+ '.tsx',
1256
+ '.ts',
1257
+ '.jsx',
1258
+ '.js'
1259
+ ];
1260
+ const isLoaderFile = possibleExtensions.some((ext)=>changedFilePath === loaderPath + ext || changedFilePath === loaderPath);
1261
+ if (isLoaderFile) {
1262
+ debug('[Loader HMR] Loader file changed, triggering reload:', changedFilePath);
1263
+ this.broadcastMessage('sendDevCommand', {
1264
+ name: 'reload'
1265
+ });
1266
+ }
1267
+ }
1268
+ }
1153
1269
  // Direct Metro access
1154
1270
  // Emulates the Metro dev server .bundle endpoint without having to go through a server.
1155
1271
  async _bundleDirectAsync(resolvedEntryFilePath, { transformOptions, resolverOptions, graphOptions, serializerOptions }) {
@@ -1367,7 +1483,7 @@ class MetroBundlerDevServer extends _BundlerDevServer.BundlerDevServer {
1367
1483
  }
1368
1484
  return (0, _getStaticRenderFunctions.evalMetroAndWrapFunctions)(this.projectRoot, res.src, res.filename, specificOptions.isExporting ?? this.instanceMetroOptions.isExporting);
1369
1485
  }, this.rscRenderer = null, this.onReloadRscEvent = null, // API Routes
1370
- this.pendingRouteOperations = new Map();
1486
+ this.pendingRouteOperations = new Map(), this.watchedLoaderFiles = new Set();
1371
1487
  }
1372
1488
  }
1373
1489
  function getBuildID(buildNumber) {