Skip to content

Commit

Permalink
feat(cli, router, babel): add router root to metro cache (expo#25658)
Browse files Browse the repository at this point in the history
# Why

- Add support for invalidating the cache when the app directory changes.
We use this feature in our E2E tests so it would be good to ensure it
works properly.
- This change also enables us to move the Expo config read operation out
of the babel preset which should generally be safer and faster.
- By adding this invalidation we also ensure that the cache is cleared
when expo-router is installed, this should help reduce issues related to
manual expo-router installation in existing projects.
- This change also deletes `expo-router/babel` in favor of
`babel-preset-expo` which is properly tested.
- I've also added the option to change the root directory to the docs
and noted that it's a dangerous operation that may lead to issue
closing.

# Test Plan

- Ensure project continue working.
- Ensure the cache clears when the root directory changes.

<!--
Please describe how you tested this change and how a reviewer could
reproduce your test, especially if this PR does not include automated
tests! If possible, please also provide terminal output and/or
screenshots demonstrating your test/reproduction.
-->

# Checklist

<!--
Please check the appropriate items below if they apply to your diff.
This is required for changes to Expo modules.
-->

- [ ] Documentation is up to date to reflect these changes (eg:
https://docs.expo.dev and README.md).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).

---------

Co-authored-by: Expo Bot <34669131+expo-bot@users.noreply.github.com>
Co-authored-by: Aman Mittal <amandeepmittal@live.com>
  • Loading branch information
3 people committed Nov 30, 2023
1 parent e8b90f6 commit 1649796
Show file tree
Hide file tree
Showing 38 changed files with 343 additions and 520 deletions.
3 changes: 1 addition & 2 deletions apps/router-e2e/app.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ module.exports = {
'expo-router',
{
asyncRoutes: process.env.E2E_ROUTER_ASYNC,
// THIS WILL BREAK YOUR APP -- DO NOT USE!
unstable_src: path.join('__e2e__', process.env.E2E_ROUTER_SRC ?? 'static-rendering', 'app'),
root: path.join('__e2e__', process.env.E2E_ROUTER_SRC ?? 'static-rendering', 'app'),
origin: 'https://smart-symbiote.netlify.app/',
},
],
Expand Down
16 changes: 0 additions & 16 deletions apps/router-e2e/metro.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
const { createMetroConfiguration } = require('expo-yarn-workspaces');
const { FileStore } = require('@expo/metro-config/file-store');

// Find the project and workspace directories
const projectRoot = __dirname;
Expand All @@ -21,19 +20,4 @@ config.resolver.blockList = [
/node_modules\/pretty-format\/node_modules\/react-is/,
];

config.cacheStores = [
// Ensure the cache isn't shared between projects
// this ensures the transform-time environment variables are changed to reflect
// the current project.
new FileStore({
root: path.join(
projectRoot,
'node_modules/.cache/metro',
process.env.E2E_ROUTER_SRC || 'app',
// TODO: Move app.json to serializer instead of babel plugin.
process.env.EXPO_E2E_BASE_PATH || '/'
),
}),
];

module.exports = config;
25 changes: 23 additions & 2 deletions docs/pages/router/reference/src-directory.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ As your project grows, it can be helpful to move all of the directories containi
files={['src/app/_layout.js', 'src/app/index.js', 'src/components/button.js', 'package.json']}
/>

Simply move your **app** directory to **src/app** and restart the development server with a clear cache:
Simply move your **app** directory to **src/app** and restart the development server.

<Terminal
cmd={['$ npx expo start --clear', '', '# Or when exporting', '$ npx expo export --clear']}
cmd={['$ npx expo start', '', '# Or export for production', '$ npx expo export']}
/>

**Notes**:
Expand All @@ -27,3 +27,24 @@ Simply move your **app** directory to **src/app** and restart the development se
- The **public** directory should remain in the root directory.
- Static rendering will automatically use the **src/app** directory if it exists.
- You may consider updating any [type aliases](/guides/typescript#path-aliases) to point to the **src** directory instead of the root directory.

## Custom directory

> Changing the default root directory is highly discouraged. We will not accept bug reports regarding projects with custom root directories.
You can dangerously customize the root directory using the Expo Router Config Plugin. The following will change the root directory to **src/routes**, relative to the project root.

```json app.json
{
"plugins": [
[
"expo-router",
{
"root": "./src/routes",
}
]
]
}
```

This may lead to unexpected behavior. Many tools assume the root directory to be either **./app** or **./src/app**. Only tools in the exact version of Expo CLI will respect the config plugin.
1 change: 1 addition & 0 deletions packages/@expo/cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

### 🎉 New features

- Add custom Expo Router root directory support with full cache invalidation on server reset. ([#25658](https://github.com/expo/expo/pull/25658) by [@EvanBacon](https://github.com/EvanBacon))
- Add filenames to metro logs when finished. ([#25575](https://github.com/expo/expo/pull/25575) by [@EvanBacon](https://github.com/EvanBacon))
- Alias `react-native-vector-icons` to `@expo/vector-icons` in the Metro resolver. ([#25512](https://github.com/expo/expo/pull/25512) by [@EvanBacon](https://github.com/EvanBacon))
- Ensure invalid production iOS builds fail more predictably. ([#25410](https://github.com/expo/expo/pull/25410) by [@EvanBacon](https://github.com/EvanBacon))
Expand Down
2 changes: 1 addition & 1 deletion packages/@expo/cli/e2e/__tests__/start-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ describe('server', () => {

// URLs
expect(manifest.launchAsset.url).toBe(
'http://127.0.0.1:8081/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&hot=false&transform.engine=hermes'
'http://127.0.0.1:8081/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&hot=false&transform.engine=hermes&transform.routerRoot=app'
);
expect(manifest.extra.expoGo.debuggerHost).toBe('127.0.0.1:8081');
expect(manifest.extra.expoGo.mainModuleName).toBe('node_modules/expo/AppEntry');
Expand Down
8 changes: 2 additions & 6 deletions packages/@expo/cli/src/export/embed/exportEmbedAsync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@ import { Options } from './resolveOptions';
import { isExecutingFromXcodebuild, logMetroErrorInXcode } from './xcodeCompilerLogger';
import { Log } from '../../log';
import { loadMetroConfigAsync } from '../../start/server/metro/instantiateMetro';
import {
getBaseUrlFromExpoConfig,
getMetroDirectBundleOptions,
} from '../../start/server/middleware/metroOptions';
import { getMetroDirectBundleOptionsForExpoConfig } from '../../start/server/middleware/metroOptions';
import { stripAnsi } from '../../utils/ansi';
import { removeAsync } from '../../utils/dir';
import { setNodeEnv } from '../../utils/nodeEnv';
Expand Down Expand Up @@ -108,13 +105,12 @@ export async function exportEmbedBundleAsync(projectRoot: string, options: Optio

const bundleRequest = {
...Server.DEFAULT_BUNDLE_OPTIONS,
...getMetroDirectBundleOptions({
...getMetroDirectBundleOptionsForExpoConfig(projectRoot, exp, {
mainModuleName: options.entryFile,
platform: options.platform,
minify: options.minify,
mode: options.dev ? 'development' : 'production',
engine: isHermes ? 'hermes' : undefined,
baseUrl: getBaseUrlFromExpoConfig(exp),
isExporting: true,
}),
sourceMapUrl,
Expand Down
2 changes: 2 additions & 0 deletions packages/@expo/cli/src/export/exportApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Options } from './resolveOptions';
import { ExportAssetMap, getFilesFromSerialAssets, persistMetroFilesAsync } from './saveAssets';
import { createAssetMap, createSourceMapDebugHtml } from './writeContents';
import * as Log from '../log';
import { getRouterDirectoryModuleIdWithManifest } from '../start/server/metro/router';
import { serializeHtmlWithAssets } from '../start/server/metro/serializeHtml';
import { getBaseUrlFromExpoConfig } from '../start/server/middleware/metroOptions';
import { createTemplateHtmlFromExpoConfigAsync } from '../start/server/webTemplate';
Expand Down Expand Up @@ -146,6 +147,7 @@ export async function exportAppAsync(
includeSourceMaps: sourceMaps,
// @ts-expect-error: server not on type yet
exportServer: exp.web?.output === 'server',
routerRoot: getRouterDirectoryModuleIdWithManifest(projectRoot, exp),
});
} else {
// TODO: Unify with exportStaticAsync
Expand Down
30 changes: 18 additions & 12 deletions packages/@expo/cli/src/export/exportStaticAsync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import { getConfig } from '@expo/config';
import assert from 'assert';
import chalk from 'chalk';
import fs from 'fs';
Expand All @@ -19,10 +18,7 @@ import { DevServerManager } from '../start/server/DevServerManager';
import { MetroBundlerDevServer } from '../start/server/metro/MetroBundlerDevServer';
import { ExpoRouterServerManifestV1 } from '../start/server/metro/fetchRouterManifest';
import { logMetroErrorAsync } from '../start/server/metro/metroErrorInterface';
import {
getApiRoutesForDirectory,
getRouterDirectoryWithManifest,
} from '../start/server/metro/router';
import { getApiRoutesForDirectory } from '../start/server/metro/router';
import { serializeHtmlWithAssets } from '../start/server/metro/serializeHtml';
import { learnMore } from '../utils/link';
import { getFreePortAsync } from '../utils/port';
Expand All @@ -38,6 +34,7 @@ type Options = {
includeSourceMaps: boolean;
entryPoint?: string;
clear: boolean;
routerRoot: string;
};

/** @private */
Expand Down Expand Up @@ -121,10 +118,17 @@ export async function getFilesToExportFromServerAsync(
async function exportFromServerAsync(
projectRoot: string,
devServerManager: DevServerManager,
{ outputDir, baseUrl, exportServer, minify, includeSourceMaps, files = new Map() }: Options
{
outputDir,
baseUrl,
exportServer,
minify,
includeSourceMaps,
routerRoot,
files = new Map(),
}: Options
): Promise<ExportAssetMap> {
const { exp } = getConfig(projectRoot, { skipSDKVersionRequirement: true });
const appDir = getRouterDirectoryWithManifest(projectRoot, exp);
const appDir = path.join(projectRoot, routerRoot);
const injectFaviconTag = await getVirtualFaviconAssetsAsync(projectRoot, {
outputDir,
baseUrl,
Expand All @@ -141,11 +145,13 @@ async function exportFromServerAsync(
minify,
includeSourceMaps,
baseUrl,
routerRoot,
}),
devServer.getStaticRenderFunctionAsync({
mode: 'production',
minify,
baseUrl,
routerRoot,
}),
]);

Expand Down Expand Up @@ -193,7 +199,7 @@ async function exportFromServerAsync(
const apiRoutes = await exportApiRoutesAsync({
outputDir,
server: devServer,
appDir,
routerRoot,
manifest: serverManifest,
baseUrl,
});
Expand Down Expand Up @@ -314,13 +320,13 @@ export function getPathVariations(routePath: string): string[] {
async function exportApiRoutesAsync({
outputDir,
server,
appDir,
routerRoot,
baseUrl,
...props
}: {
outputDir: string;
server: MetroBundlerDevServer;
appDir: string;
routerRoot: string;
manifest: ExpoRouterServerManifestV1;
baseUrl: string;
}): Promise<ExportAssetMap> {
Expand All @@ -330,7 +336,7 @@ async function exportApiRoutesAsync({

const { manifest, files } = await server.exportExpoRouterApiRoutesAsync({
mode: 'production',
appDir,
routerRoot,
outputDir: functionsDir,
prerenderManifest: props.manifest,
baseUrl,
Expand Down
6 changes: 2 additions & 4 deletions packages/@expo/cli/src/export/fork-bundleAsync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ import { loadMetroConfigAsync } from '../start/server/metro/instantiateMetro';
import { getEntryWithServerRoot } from '../start/server/middleware/ManifestMiddleware';
import {
ExpoMetroBundleOptions,
getBaseUrlFromExpoConfig,
getMetroDirectBundleOptions,
getMetroDirectBundleOptionsForExpoConfig,
} from '../start/server/middleware/metroOptions';

export type MetroDevServerOptions = LoadOptions;
Expand Down Expand Up @@ -137,7 +136,7 @@ async function bundleProductionMetroClientAsync(
const bundleOptions: MetroBundleOptions = {
...Server.DEFAULT_BUNDLE_OPTIONS,
sourceMapUrl: bundle.sourceMapUrl,
...getMetroDirectBundleOptions({
...getMetroDirectBundleOptionsForExpoConfig(projectRoot, expoConfig, {
mainModuleName: bundle.entryPoint,
platform: bundle.platform,
mode: bundle.dev ? 'development' : 'production',
Expand All @@ -147,7 +146,6 @@ async function bundleProductionMetroClientAsync(
// serializerOutput: bundle.platform === 'web' ? 'static' : undefined,
serializerOutput: 'static',
serializerIncludeBytecode: isHermes,
baseUrl: getBaseUrlFromExpoConfig(expoConfig),
isExporting: true,
}),
bundleType: 'bundle',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ type StaticRenderOptions = {
environment?: 'node';
engine?: 'hermes';
baseUrl: string;
routerRoot: string;
};

const moveStaticRenderFunction = memoize(async (projectRoot: string, requiredModuleId: string) => {
Expand All @@ -66,7 +67,7 @@ const moveStaticRenderFunction = memoize(async (projectRoot: string, requiredMod
async function getStaticRenderFunctionsContentAsync(
projectRoot: string,
devServerUrl: string,
{ dev = false, minify = false, environment, baseUrl }: StaticRenderOptions
{ dev = false, minify = false, environment, baseUrl, routerRoot }: StaticRenderOptions
): Promise<string> {
const root = getMetroServerRoot(projectRoot);
const requiredModuleId = getRenderModuleId(root);
Expand All @@ -83,6 +84,7 @@ async function getStaticRenderFunctionsContentAsync(
minify,
environment,
baseUrl,
routerRoot,
});
}

Expand Down Expand Up @@ -115,6 +117,7 @@ export async function createMetroEndpointAsync(
environment,
engine = 'hermes',
baseUrl,
routerRoot,
}: StaticRenderOptions
): Promise<string> {
const root = getMetroServerRoot(projectRoot);
Expand All @@ -131,6 +134,7 @@ export async function createMetroEndpointAsync(
minify,
baseUrl,
isExporting: true,
routerRoot,
});

const url = new URL(urlFragment.replace(/^\//, ''), devServerUrl).toString();
Expand Down

0 comments on commit 1649796

Please sign in to comment.